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.
- data/.gitignore +17 -0
- data/.ruby-version +1 -0
- data/.travis.yml +14 -0
- data/CONTRIBUTING.md +22 -0
- data/Gemfile +4 -0
- data/LICENSE +20 -0
- data/README.md +5 -0
- data/Rakefile +46 -0
- data/app/models/action.rb +60 -0
- data/app/models/answer_edition.rb +13 -0
- data/app/models/artefact.rb +341 -0
- data/app/models/artefact_action.rb +27 -0
- data/app/models/artefact_external_link.rb +15 -0
- data/app/models/business_support/business_size.rb +14 -0
- data/app/models/business_support/business_type.rb +14 -0
- data/app/models/business_support/location.rb +14 -0
- data/app/models/business_support/purpose.rb +14 -0
- data/app/models/business_support/sector.rb +14 -0
- data/app/models/business_support/stage.rb +14 -0
- data/app/models/business_support/support_type.rb +14 -0
- data/app/models/business_support_edition.rb +69 -0
- data/app/models/campaign_edition.rb +72 -0
- data/app/models/completed_transaction_edition.rb +14 -0
- data/app/models/curated_list.rb +32 -0
- data/app/models/edition.rb +286 -0
- data/app/models/expectant.rb +21 -0
- data/app/models/expectation.rb +12 -0
- data/app/models/guide_edition.rb +19 -0
- data/app/models/help_page_edition.rb +13 -0
- data/app/models/licence_edition.rb +35 -0
- data/app/models/local_authority.rb +58 -0
- data/app/models/local_interaction.rb +20 -0
- data/app/models/local_service.rb +49 -0
- data/app/models/local_transaction_edition.rb +49 -0
- data/app/models/overview_dashboard.rb +25 -0
- data/app/models/part.rb +28 -0
- data/app/models/parted.rb +32 -0
- data/app/models/place_edition.rb +20 -0
- data/app/models/programme_edition.rb +26 -0
- data/app/models/simple_smart_answer_edition.rb +66 -0
- data/app/models/simple_smart_answer_edition/node.rb +40 -0
- data/app/models/simple_smart_answer_edition/node/option.rb +31 -0
- data/app/models/tag.rb +88 -0
- data/app/models/transaction_edition.rb +28 -0
- data/app/models/travel_advice_edition.rb +177 -0
- data/app/models/user.rb +54 -0
- data/app/models/video_edition.rb +24 -0
- data/app/models/workflow.rb +217 -0
- data/app/models/workflow_actor.rb +141 -0
- data/app/traits/attachable.rb +60 -0
- data/app/traits/govspeak_smart_quotes_fixer.rb +19 -0
- data/app/traits/taggable.rb +113 -0
- data/app/validators/safe_html.rb +33 -0
- data/app/validators/slug_validator.rb +53 -0
- data/config/mongoid.yml +5 -0
- data/govuk_content_models.gemspec +42 -0
- data/jenkins.sh +7 -0
- data/lib/fact_check_address.rb +36 -0
- data/lib/govuk_content_models.rb +12 -0
- data/lib/govuk_content_models/require_all.rb +14 -0
- data/lib/govuk_content_models/test_helpers/factories.rb +213 -0
- data/lib/govuk_content_models/test_helpers/local_services.rb +24 -0
- data/lib/govuk_content_models/version.rb +4 -0
- data/test/fixtures/contactotron_api_response.json +1 -0
- data/test/fixtures/uploads/image.jpg +0 -0
- data/test/models/artefact_action_test.rb +123 -0
- data/test/models/artefact_external_link_test.rb +32 -0
- data/test/models/artefact_tag_test.rb +52 -0
- data/test/models/artefact_test.rb +583 -0
- data/test/models/business_support/business_size_test.rb +25 -0
- data/test/models/business_support/business_type_test.rb +25 -0
- data/test/models/business_support/location_test.rb +25 -0
- data/test/models/business_support/purpose_test.rb +29 -0
- data/test/models/business_support/sector_test.rb +25 -0
- data/test/models/business_support/stage_test.rb +25 -0
- data/test/models/business_support/support_type_test.rb +25 -0
- data/test/models/business_support_edition_test.rb +186 -0
- data/test/models/campaign_edition_test.rb +90 -0
- data/test/models/curated_list_test.rb +32 -0
- data/test/models/edition_test.rb +826 -0
- data/test/models/fact_check_address_test.rb +36 -0
- data/test/models/help_page_edition_test.rb +38 -0
- data/test/models/licence_edition_test.rb +104 -0
- data/test/models/local_authority_test.rb +113 -0
- data/test/models/local_service_test.rb +199 -0
- data/test/models/local_transaction_edition_test.rb +78 -0
- data/test/models/overview_dashboard_test.rb +47 -0
- data/test/models/simple_smart_answer_edition_test.rb +169 -0
- data/test/models/simple_smart_answer_node_test.rb +134 -0
- data/test/models/simple_smart_answer_option_test.rb +90 -0
- data/test/models/tag_test.rb +92 -0
- data/test/models/time_zone_test.rb +48 -0
- data/test/models/transaction_edition_test.rb +20 -0
- data/test/models/travel_advice_edition_test.rb +480 -0
- data/test/models/user_test.rb +114 -0
- data/test/models/video_edition_test.rb +64 -0
- data/test/models/workflow_actor_test.rb +61 -0
- data/test/models/workflow_test.rb +307 -0
- data/test/test_helper.rb +47 -0
- data/test/traits/attachable_test.rb +143 -0
- data/test/traits/taggable_test.rb +114 -0
- data/test/validators/safe_html_validator_test.rb +86 -0
- data/test/validators/slug_validator_test.rb +42 -0
- 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
|