govuk_content_models 6.0.2
Sign up to get free protection for your applications and to get access to all the features.
- 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
data/test/test_helper.rb
ADDED
@@ -0,0 +1,47 @@
|
|
1
|
+
ENV["RACK_ENV"] = "test"
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
|
5
|
+
require "active_support/test_case"
|
6
|
+
require "shoulda/context"
|
7
|
+
require "minitest/autorun"
|
8
|
+
require "mongoid"
|
9
|
+
require "govuk_content_models/require_all"
|
10
|
+
require "database_cleaner"
|
11
|
+
require "gds_api/test_helpers/panopticon"
|
12
|
+
require "webmock/test_unit"
|
13
|
+
require "govuk_content_models/test_helpers/factories"
|
14
|
+
require "timecop"
|
15
|
+
|
16
|
+
# The models depend on a zone being set, so tests will fail if we don't
|
17
|
+
Time.zone = "London"
|
18
|
+
|
19
|
+
Mongoid.load! File.expand_path("../../config/mongoid.yml", __FILE__)
|
20
|
+
WebMock.disable_net_connect!
|
21
|
+
|
22
|
+
DatabaseCleaner.strategy = :truncation
|
23
|
+
# initial clean
|
24
|
+
DatabaseCleaner.clean
|
25
|
+
|
26
|
+
class ActiveSupport::TestCase
|
27
|
+
PROJECT_ROOT = File.expand_path("../..", __FILE__)
|
28
|
+
|
29
|
+
include GdsApi::TestHelpers::Panopticon
|
30
|
+
|
31
|
+
def without_metadata_denormalisation(*klasses, &block)
|
32
|
+
klasses.each {|klass| klass.any_instance.stubs(:denormalise_metadata).returns(true) }
|
33
|
+
result = yield
|
34
|
+
klasses.each {|klass| klass.any_instance.unstub(:denormalise_metadata) }
|
35
|
+
result
|
36
|
+
end
|
37
|
+
|
38
|
+
def clean_db
|
39
|
+
DatabaseCleaner.clean
|
40
|
+
end
|
41
|
+
set_callback :teardown, :before, :clean_db
|
42
|
+
|
43
|
+
def timecop_return
|
44
|
+
Timecop.return
|
45
|
+
end
|
46
|
+
set_callback :teardown, :before, :timecop_return
|
47
|
+
end
|
@@ -0,0 +1,143 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class MockAssetApi
|
4
|
+
class MockError < StandardError; end
|
5
|
+
end
|
6
|
+
|
7
|
+
class ModelWithAttachments
|
8
|
+
include Attachable
|
9
|
+
include Mongoid::Document
|
10
|
+
|
11
|
+
field :title, type: String
|
12
|
+
attaches :image
|
13
|
+
end
|
14
|
+
|
15
|
+
class AttachableTest < ActiveSupport::TestCase
|
16
|
+
|
17
|
+
setup do
|
18
|
+
@edition = ModelWithAttachments.new
|
19
|
+
@previous_api_client = Attachable.asset_api_client
|
20
|
+
Attachable.asset_api_client = MockAssetApi.new
|
21
|
+
end
|
22
|
+
|
23
|
+
teardown do
|
24
|
+
Attachable.asset_api_client = @previous_api_client
|
25
|
+
end
|
26
|
+
|
27
|
+
context "retreiving assets from the api" do
|
28
|
+
should "raise an exception if there is no api client present" do
|
29
|
+
Attachable.asset_api_client = nil
|
30
|
+
|
31
|
+
@edition.image_id = "an_image_id"
|
32
|
+
assert_raise Attachable::ApiClientNotPresent do
|
33
|
+
@edition.image.file_url
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
should "make the request to the asset api" do
|
38
|
+
@edition.image_id = "an_image_id"
|
39
|
+
|
40
|
+
asset = OpenStruct.new(:file_url => "/path/to/image")
|
41
|
+
MockAssetApi.any_instance.expects(:asset).with("an_image_id").returns(asset)
|
42
|
+
|
43
|
+
assert_equal "/path/to/image", @edition.image.file_url
|
44
|
+
end
|
45
|
+
|
46
|
+
should "cache the asset from the api" do
|
47
|
+
@edition.image_id = "an_image_id"
|
48
|
+
|
49
|
+
asset = OpenStruct.new(:something => "one", :something_else => "two")
|
50
|
+
MockAssetApi.any_instance.expects(:asset).once.with("an_image_id").returns(asset)
|
51
|
+
|
52
|
+
assert_equal "one", @edition.image.something
|
53
|
+
assert_equal "two", @edition.image.something_else
|
54
|
+
end
|
55
|
+
|
56
|
+
should "assign a file and detect it has changed" do
|
57
|
+
file = File.open(File.expand_path("../../fixtures/uploads/image.jpg", __FILE__))
|
58
|
+
|
59
|
+
@edition.image = file
|
60
|
+
assert @edition.image_has_changed?
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
context "saving an edition" do
|
65
|
+
setup do
|
66
|
+
@file = File.open(File.expand_path("../../fixtures/uploads/image.jpg", __FILE__))
|
67
|
+
@asset = OpenStruct.new(:id => 'http://asset-manager.dev.gov.uk/assets/an_image_id')
|
68
|
+
end
|
69
|
+
|
70
|
+
should "upload the asset" do
|
71
|
+
MockAssetApi.any_instance.expects(:create_asset).with({ :file => @file }).returns(@asset)
|
72
|
+
|
73
|
+
@edition.image = @file
|
74
|
+
@edition.save!
|
75
|
+
end
|
76
|
+
|
77
|
+
should "not upload an asset if it has not changed" do
|
78
|
+
ModelWithAttachments.any_instance.expects(:upload_image).never
|
79
|
+
@edition.save!
|
80
|
+
end
|
81
|
+
|
82
|
+
should "assign the asset id to the attachment id attribute" do
|
83
|
+
MockAssetApi.any_instance.expects(:create_asset).with({ :file => @file }).returns(@asset)
|
84
|
+
|
85
|
+
@edition.image = @file
|
86
|
+
@edition.save!
|
87
|
+
|
88
|
+
assert_equal "an_image_id", @edition.image_id
|
89
|
+
end
|
90
|
+
|
91
|
+
should "raise an exception if there is no api client present" do
|
92
|
+
Attachable.asset_api_client = nil
|
93
|
+
|
94
|
+
@edition.image = @file
|
95
|
+
assert_raise Attachable::ApiClientNotPresent do
|
96
|
+
@edition.save!
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
should "catch any errors raised by the api client" do
|
101
|
+
MockAssetApi.any_instance.expects(:create_asset).raises(MockAssetApi::MockError)
|
102
|
+
|
103
|
+
assert_nothing_raised do
|
104
|
+
@edition.image = @file
|
105
|
+
@edition.save!
|
106
|
+
end
|
107
|
+
|
108
|
+
assert_equal ["could not be uploaded"], @edition.errors[:image_id]
|
109
|
+
end
|
110
|
+
|
111
|
+
should "not stop the edition from being saved when an uploading error is raised" do
|
112
|
+
MockAssetApi.any_instance.expects(:create_asset).raises(MockAssetApi::MockError)
|
113
|
+
|
114
|
+
@edition.image = @file
|
115
|
+
@edition.title = "foo"
|
116
|
+
@edition.save!
|
117
|
+
|
118
|
+
@edition.reload
|
119
|
+
assert_equal "foo", @edition.title
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
context "removing an asset" do
|
124
|
+
should "remove an asset when remove_* set to true" do
|
125
|
+
@edition.image_id = 'an_image_id'
|
126
|
+
@edition.remove_image = true
|
127
|
+
@edition.save!
|
128
|
+
|
129
|
+
assert_nil @edition.image_id
|
130
|
+
end
|
131
|
+
|
132
|
+
should "not remove an asset when remove_* set to false or empty" do
|
133
|
+
@edition.image_id = 'an_image_id'
|
134
|
+
@edition.remove_image = false
|
135
|
+
@edition.remove_image = ""
|
136
|
+
@edition.remove_image = nil
|
137
|
+
@edition.save!
|
138
|
+
|
139
|
+
assert_equal "an_image_id", @edition.image_id
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
end
|
@@ -0,0 +1,114 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
# This test relies on the fact that artefact uses the taggable module
|
4
|
+
class TaggableTest < ActiveSupport::TestCase
|
5
|
+
|
6
|
+
TEST_SECTIONS = [['crime', 'Crime'], ['crime/the-police', 'The Police'],
|
7
|
+
['crime/batman', 'Batman']]
|
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
|
+
|
18
|
+
@item = FactoryGirl.create(:artefact)
|
19
|
+
end
|
20
|
+
|
21
|
+
test "can set sections" do
|
22
|
+
@item.sections = ['crime', 'crime/the-police']
|
23
|
+
@item.reconcile_tag_ids
|
24
|
+
|
25
|
+
assert_equal ['crime', 'crime/the-police'], @item.tag_ids, 'Mismatched tags'
|
26
|
+
assert_equal ['crime', 'crime/the-police'], @item.sections.collect(&:tag_id), 'Mismatched sections'
|
27
|
+
|
28
|
+
assert_equal 'Crime', @item.primary_section.title
|
29
|
+
end
|
30
|
+
|
31
|
+
test "can set sections and primary section separately" do
|
32
|
+
@item.sections = ['crime', 'crime/the-police']
|
33
|
+
@item.primary_section = 'crime'
|
34
|
+
@item.reconcile_tag_ids
|
35
|
+
|
36
|
+
assert_equal ['crime', 'crime/the-police'], @item.tag_ids, 'Mismatched tags'
|
37
|
+
assert_equal ['crime', 'crime/the-police'], @item.sections.collect(&:tag_id), 'Mismatched sections'
|
38
|
+
|
39
|
+
assert_equal 'Crime', @item.primary_section.title
|
40
|
+
end
|
41
|
+
|
42
|
+
test "can set subsection as primary section" do
|
43
|
+
@item.sections = ['crime/the-police', 'crime']
|
44
|
+
@item.primary_section = 'crime/the-police'
|
45
|
+
@item.reconcile_tag_ids
|
46
|
+
assert_equal 'The Police', @item.primary_section.title
|
47
|
+
end
|
48
|
+
|
49
|
+
test "cannot set non-existent sections" do
|
50
|
+
assert_raise Tag::MissingTags do
|
51
|
+
@item.sections = ['weevils']
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
test "cannot set non-section tags" do
|
56
|
+
assert_raise Tag::MissingTags do
|
57
|
+
@item.sections = ['crime', 'bacon']
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
test "can set no sections" do
|
62
|
+
@item.sections = ['crime', 'crime/the-police']
|
63
|
+
@item.sections = []
|
64
|
+
assert_equal [], @item.sections
|
65
|
+
|
66
|
+
refute @item.primary_section
|
67
|
+
end
|
68
|
+
|
69
|
+
test "setting sections doesn't break other tags" do
|
70
|
+
@item.keywords = ['cheese', 'bacon']
|
71
|
+
@item.sections = ['crime']
|
72
|
+
@item.primary_section = 'crime'
|
73
|
+
@item.reconcile_tag_ids
|
74
|
+
|
75
|
+
assert_equal ['bacon', 'cheese', 'crime'], @item.tag_ids.sort
|
76
|
+
assert_equal 'Crime', @item.primary_section.title
|
77
|
+
end
|
78
|
+
|
79
|
+
test "setting primary section adds section to tags" do
|
80
|
+
@item.sections = ['crime', 'crime/the-police']
|
81
|
+
@item.primary_section = 'crime/batman'
|
82
|
+
@item.reconcile_tag_ids
|
83
|
+
assert_includes @item.sections.collect(&:tag_id), 'crime/batman'
|
84
|
+
end
|
85
|
+
|
86
|
+
test "setting primary section to existing section works" do
|
87
|
+
@item.sections = ['crime', 'crime/the-police']
|
88
|
+
@item.primary_section = 'crime/the-police'
|
89
|
+
@item.reconcile_tag_ids
|
90
|
+
# Note: not testing the order of the sections in this test, just testing
|
91
|
+
# that the section is still present and not duplicated
|
92
|
+
assert_equal ['crime', 'crime/the-police'], @item.sections.collect(&:tag_id).sort
|
93
|
+
end
|
94
|
+
|
95
|
+
test "setting primary section to existing section reorders correctly" do
|
96
|
+
@item.sections = ['crime', 'crime/the-police']
|
97
|
+
@item.save!
|
98
|
+
|
99
|
+
@item.primary_section = 'crime/batman'
|
100
|
+
@item.save!
|
101
|
+
|
102
|
+
assert_equal 'crime/batman', @item.primary_section.tag_id
|
103
|
+
assert_equal ['crime/batman', 'crime', 'crime/the-police'], @item.sections.collect(&:tag_id)
|
104
|
+
end
|
105
|
+
|
106
|
+
test "can set tags using foo_ids= type method" do
|
107
|
+
@item.keyword_ids = ['bacon']
|
108
|
+
@item.save!
|
109
|
+
|
110
|
+
@item.reload
|
111
|
+
|
112
|
+
assert_equal ['bacon'], @item.keyword_ids
|
113
|
+
end
|
114
|
+
end
|
@@ -0,0 +1,86 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class SafeHtmlTest < ActiveSupport::TestCase
|
4
|
+
class Dummy
|
5
|
+
include Mongoid::Document
|
6
|
+
|
7
|
+
field "declared", type: String
|
8
|
+
field "i_am_govspeak", type: String
|
9
|
+
|
10
|
+
GOVSPEAK_FIELDS = [:i_am_govspeak]
|
11
|
+
|
12
|
+
validates_with SafeHtml
|
13
|
+
|
14
|
+
embeds_one :dummy_embedded_single, class_name: 'SafeHtmlTest::DummyEmbeddedSingle'
|
15
|
+
end
|
16
|
+
|
17
|
+
class DummyEmbeddedSingle
|
18
|
+
include Mongoid::Document
|
19
|
+
|
20
|
+
GOVSPEAK_FIELDS = []
|
21
|
+
|
22
|
+
validates_with SafeHtml
|
23
|
+
|
24
|
+
embedded_in :dummy, class_name: 'SafeHtmlTest::Dummy'
|
25
|
+
end
|
26
|
+
|
27
|
+
context "we don't quite trust mongoid (2)" do
|
28
|
+
should "embedded documents should be validated automatically" do
|
29
|
+
embedded = DummyEmbeddedSingle.new(dirty: "<script>")
|
30
|
+
dummy = Dummy.new(dummy_embedded_single: embedded)
|
31
|
+
# Can't invoke embedded.valid? because that would run the validations
|
32
|
+
assert dummy.invalid?
|
33
|
+
assert_includes dummy.errors.keys, :dummy_embedded_single
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
context "what to validate" do
|
38
|
+
should "test declared fields" do
|
39
|
+
dummy = Dummy.new(declared: "<script>alert('XSS')</script>")
|
40
|
+
assert dummy.invalid?
|
41
|
+
assert_includes dummy.errors.keys, :declared
|
42
|
+
end
|
43
|
+
|
44
|
+
should "test undeclared fields" do
|
45
|
+
dummy = Dummy.new(undeclared: "<script>")
|
46
|
+
assert dummy.invalid?
|
47
|
+
assert_includes dummy.errors.keys, :undeclared
|
48
|
+
end
|
49
|
+
|
50
|
+
should "allow clean content in nested fields" do
|
51
|
+
dummy = Dummy.new(undeclared: { "clean" => ["plain text"] })
|
52
|
+
assert dummy.valid?
|
53
|
+
end
|
54
|
+
|
55
|
+
should "disallow dirty content in nested fields" do
|
56
|
+
dummy = Dummy.new(undeclared: { "dirty" => ["<script>"] })
|
57
|
+
assert dummy.invalid?
|
58
|
+
assert_includes dummy.errors.keys, :undeclared
|
59
|
+
end
|
60
|
+
|
61
|
+
should "allow plain text" do
|
62
|
+
dummy = Dummy.new(declared: "foo bar")
|
63
|
+
assert dummy.valid?
|
64
|
+
end
|
65
|
+
|
66
|
+
should "check only specified fields as Govspeak" do
|
67
|
+
nasty_govspeak = %q{[Numberwang](script:nasty(); "Wangernum")}
|
68
|
+
assert ! Govspeak::Document.new(nasty_govspeak).valid?, "expected this to be identified as bad"
|
69
|
+
dummy = Dummy.new(i_am_govspeak: nasty_govspeak)
|
70
|
+
assert dummy.invalid?
|
71
|
+
end
|
72
|
+
|
73
|
+
should "all models should use this validator" do
|
74
|
+
models_dir = File.expand_path("../../app/models/*", File.dirname(__FILE__))
|
75
|
+
|
76
|
+
classes = Dir[models_dir].map do |file|
|
77
|
+
klass = File.basename(file, ".rb").camelize.constantize
|
78
|
+
klass.included_modules.include?(Mongoid::Document) ? klass : nil
|
79
|
+
end.compact
|
80
|
+
|
81
|
+
classes.each do |klass|
|
82
|
+
assert_includes klass.validators.map(&:class), SafeHtml, "#{klass} must be validated with SafeHtml"
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class SlugTest < ActiveSupport::TestCase
|
4
|
+
class Dummy
|
5
|
+
include Mongoid::Document
|
6
|
+
|
7
|
+
field "name", type: String
|
8
|
+
field "slug", type: String
|
9
|
+
field "kind", type: String
|
10
|
+
|
11
|
+
validates :name, presence: true
|
12
|
+
validates :slug, presence: true, uniqueness: true, slug: true
|
13
|
+
end
|
14
|
+
|
15
|
+
context "Slugs are checked" do
|
16
|
+
should "validate slugs for normal documents" do
|
17
|
+
record = Dummy.new(name: "Test", slug: "test")
|
18
|
+
assert record.valid?
|
19
|
+
end
|
20
|
+
|
21
|
+
should "validate help pages as starting with /help" do
|
22
|
+
record = Dummy.new(name: "Help 1", slug: "test", kind: "help_page")
|
23
|
+
assert record.invalid?
|
24
|
+
|
25
|
+
record.slug = "help/test"
|
26
|
+
assert record.valid?
|
27
|
+
end
|
28
|
+
|
29
|
+
should "validate inside government slugs as containing /government" do
|
30
|
+
record = Dummy.new(name: "Test 2", slug: "test", kind: "policy")
|
31
|
+
assert record.invalid?
|
32
|
+
|
33
|
+
record.slug = "government/test"
|
34
|
+
assert record.valid?
|
35
|
+
end
|
36
|
+
|
37
|
+
should "allow friendly_id suffixes to pass" do
|
38
|
+
record = Dummy.new(name: "Test 3", slug: "government/policy/test--3", kind: "policy")
|
39
|
+
assert record.valid?
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
metadata
ADDED
@@ -0,0 +1,511 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: govuk_content_models
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 6.0.2
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Paul Battley
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2014-01-06 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: bson_ext
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ! '>='
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '0'
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: differ
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ! '>='
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: '0'
|
38
|
+
type: :runtime
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ! '>='
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '0'
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: gds-api-adapters
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - ! '>='
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '0'
|
54
|
+
type: :runtime
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ! '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
- !ruby/object:Gem::Dependency
|
63
|
+
name: gds-sso
|
64
|
+
requirement: !ruby/object:Gem::Requirement
|
65
|
+
none: false
|
66
|
+
requirements:
|
67
|
+
- - ! '>='
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: 3.0.0
|
70
|
+
- - <
|
71
|
+
- !ruby/object:Gem::Version
|
72
|
+
version: 4.0.0
|
73
|
+
type: :runtime
|
74
|
+
prerelease: false
|
75
|
+
version_requirements: !ruby/object:Gem::Requirement
|
76
|
+
none: false
|
77
|
+
requirements:
|
78
|
+
- - ! '>='
|
79
|
+
- !ruby/object:Gem::Version
|
80
|
+
version: 3.0.0
|
81
|
+
- - <
|
82
|
+
- !ruby/object:Gem::Version
|
83
|
+
version: 4.0.0
|
84
|
+
- !ruby/object:Gem::Dependency
|
85
|
+
name: govspeak
|
86
|
+
requirement: !ruby/object:Gem::Requirement
|
87
|
+
none: false
|
88
|
+
requirements:
|
89
|
+
- - ! '>='
|
90
|
+
- !ruby/object:Gem::Version
|
91
|
+
version: 1.0.1
|
92
|
+
- - <
|
93
|
+
- !ruby/object:Gem::Version
|
94
|
+
version: 2.0.0
|
95
|
+
type: :runtime
|
96
|
+
prerelease: false
|
97
|
+
version_requirements: !ruby/object:Gem::Requirement
|
98
|
+
none: false
|
99
|
+
requirements:
|
100
|
+
- - ! '>='
|
101
|
+
- !ruby/object:Gem::Version
|
102
|
+
version: 1.0.1
|
103
|
+
- - <
|
104
|
+
- !ruby/object:Gem::Version
|
105
|
+
version: 2.0.0
|
106
|
+
- !ruby/object:Gem::Dependency
|
107
|
+
name: mongoid
|
108
|
+
requirement: !ruby/object:Gem::Requirement
|
109
|
+
none: false
|
110
|
+
requirements:
|
111
|
+
- - ~>
|
112
|
+
- !ruby/object:Gem::Version
|
113
|
+
version: '2.5'
|
114
|
+
type: :runtime
|
115
|
+
prerelease: false
|
116
|
+
version_requirements: !ruby/object:Gem::Requirement
|
117
|
+
none: false
|
118
|
+
requirements:
|
119
|
+
- - ~>
|
120
|
+
- !ruby/object:Gem::Version
|
121
|
+
version: '2.5'
|
122
|
+
- !ruby/object:Gem::Dependency
|
123
|
+
name: plek
|
124
|
+
requirement: !ruby/object:Gem::Requirement
|
125
|
+
none: false
|
126
|
+
requirements:
|
127
|
+
- - ! '>='
|
128
|
+
- !ruby/object:Gem::Version
|
129
|
+
version: '0'
|
130
|
+
type: :runtime
|
131
|
+
prerelease: false
|
132
|
+
version_requirements: !ruby/object:Gem::Requirement
|
133
|
+
none: false
|
134
|
+
requirements:
|
135
|
+
- - ! '>='
|
136
|
+
- !ruby/object:Gem::Version
|
137
|
+
version: '0'
|
138
|
+
- !ruby/object:Gem::Dependency
|
139
|
+
name: state_machine
|
140
|
+
requirement: !ruby/object:Gem::Requirement
|
141
|
+
none: false
|
142
|
+
requirements:
|
143
|
+
- - ! '>='
|
144
|
+
- !ruby/object:Gem::Version
|
145
|
+
version: '0'
|
146
|
+
type: :runtime
|
147
|
+
prerelease: false
|
148
|
+
version_requirements: !ruby/object:Gem::Requirement
|
149
|
+
none: false
|
150
|
+
requirements:
|
151
|
+
- - ! '>='
|
152
|
+
- !ruby/object:Gem::Version
|
153
|
+
version: '0'
|
154
|
+
- !ruby/object:Gem::Dependency
|
155
|
+
name: database_cleaner
|
156
|
+
requirement: !ruby/object:Gem::Requirement
|
157
|
+
none: false
|
158
|
+
requirements:
|
159
|
+
- - '='
|
160
|
+
- !ruby/object:Gem::Version
|
161
|
+
version: 0.7.2
|
162
|
+
type: :development
|
163
|
+
prerelease: false
|
164
|
+
version_requirements: !ruby/object:Gem::Requirement
|
165
|
+
none: false
|
166
|
+
requirements:
|
167
|
+
- - '='
|
168
|
+
- !ruby/object:Gem::Version
|
169
|
+
version: 0.7.2
|
170
|
+
- !ruby/object:Gem::Dependency
|
171
|
+
name: factory_girl
|
172
|
+
requirement: !ruby/object:Gem::Requirement
|
173
|
+
none: false
|
174
|
+
requirements:
|
175
|
+
- - '='
|
176
|
+
- !ruby/object:Gem::Version
|
177
|
+
version: 3.3.0
|
178
|
+
type: :development
|
179
|
+
prerelease: false
|
180
|
+
version_requirements: !ruby/object:Gem::Requirement
|
181
|
+
none: false
|
182
|
+
requirements:
|
183
|
+
- - '='
|
184
|
+
- !ruby/object:Gem::Version
|
185
|
+
version: 3.3.0
|
186
|
+
- !ruby/object:Gem::Dependency
|
187
|
+
name: gem_publisher
|
188
|
+
requirement: !ruby/object:Gem::Requirement
|
189
|
+
none: false
|
190
|
+
requirements:
|
191
|
+
- - '='
|
192
|
+
- !ruby/object:Gem::Version
|
193
|
+
version: 1.2.0
|
194
|
+
type: :development
|
195
|
+
prerelease: false
|
196
|
+
version_requirements: !ruby/object:Gem::Requirement
|
197
|
+
none: false
|
198
|
+
requirements:
|
199
|
+
- - '='
|
200
|
+
- !ruby/object:Gem::Version
|
201
|
+
version: 1.2.0
|
202
|
+
- !ruby/object:Gem::Dependency
|
203
|
+
name: mocha
|
204
|
+
requirement: !ruby/object:Gem::Requirement
|
205
|
+
none: false
|
206
|
+
requirements:
|
207
|
+
- - '='
|
208
|
+
- !ruby/object:Gem::Version
|
209
|
+
version: 0.13.3
|
210
|
+
type: :development
|
211
|
+
prerelease: false
|
212
|
+
version_requirements: !ruby/object:Gem::Requirement
|
213
|
+
none: false
|
214
|
+
requirements:
|
215
|
+
- - '='
|
216
|
+
- !ruby/object:Gem::Version
|
217
|
+
version: 0.13.3
|
218
|
+
- !ruby/object:Gem::Dependency
|
219
|
+
name: multi_json
|
220
|
+
requirement: !ruby/object:Gem::Requirement
|
221
|
+
none: false
|
222
|
+
requirements:
|
223
|
+
- - '='
|
224
|
+
- !ruby/object:Gem::Version
|
225
|
+
version: 1.3.7
|
226
|
+
type: :development
|
227
|
+
prerelease: false
|
228
|
+
version_requirements: !ruby/object:Gem::Requirement
|
229
|
+
none: false
|
230
|
+
requirements:
|
231
|
+
- - '='
|
232
|
+
- !ruby/object:Gem::Version
|
233
|
+
version: 1.3.7
|
234
|
+
- !ruby/object:Gem::Dependency
|
235
|
+
name: rake
|
236
|
+
requirement: !ruby/object:Gem::Requirement
|
237
|
+
none: false
|
238
|
+
requirements:
|
239
|
+
- - '='
|
240
|
+
- !ruby/object:Gem::Version
|
241
|
+
version: 0.9.2.2
|
242
|
+
type: :development
|
243
|
+
prerelease: false
|
244
|
+
version_requirements: !ruby/object:Gem::Requirement
|
245
|
+
none: false
|
246
|
+
requirements:
|
247
|
+
- - '='
|
248
|
+
- !ruby/object:Gem::Version
|
249
|
+
version: 0.9.2.2
|
250
|
+
- !ruby/object:Gem::Dependency
|
251
|
+
name: webmock
|
252
|
+
requirement: !ruby/object:Gem::Requirement
|
253
|
+
none: false
|
254
|
+
requirements:
|
255
|
+
- - '='
|
256
|
+
- !ruby/object:Gem::Version
|
257
|
+
version: 1.8.7
|
258
|
+
type: :development
|
259
|
+
prerelease: false
|
260
|
+
version_requirements: !ruby/object:Gem::Requirement
|
261
|
+
none: false
|
262
|
+
requirements:
|
263
|
+
- - '='
|
264
|
+
- !ruby/object:Gem::Version
|
265
|
+
version: 1.8.7
|
266
|
+
- !ruby/object:Gem::Dependency
|
267
|
+
name: shoulda-context
|
268
|
+
requirement: !ruby/object:Gem::Requirement
|
269
|
+
none: false
|
270
|
+
requirements:
|
271
|
+
- - '='
|
272
|
+
- !ruby/object:Gem::Version
|
273
|
+
version: 1.0.0
|
274
|
+
type: :development
|
275
|
+
prerelease: false
|
276
|
+
version_requirements: !ruby/object:Gem::Requirement
|
277
|
+
none: false
|
278
|
+
requirements:
|
279
|
+
- - '='
|
280
|
+
- !ruby/object:Gem::Version
|
281
|
+
version: 1.0.0
|
282
|
+
- !ruby/object:Gem::Dependency
|
283
|
+
name: timecop
|
284
|
+
requirement: !ruby/object:Gem::Requirement
|
285
|
+
none: false
|
286
|
+
requirements:
|
287
|
+
- - '='
|
288
|
+
- !ruby/object:Gem::Version
|
289
|
+
version: 0.5.9.2
|
290
|
+
type: :development
|
291
|
+
prerelease: false
|
292
|
+
version_requirements: !ruby/object:Gem::Requirement
|
293
|
+
none: false
|
294
|
+
requirements:
|
295
|
+
- - '='
|
296
|
+
- !ruby/object:Gem::Version
|
297
|
+
version: 0.5.9.2
|
298
|
+
- !ruby/object:Gem::Dependency
|
299
|
+
name: rack
|
300
|
+
requirement: !ruby/object:Gem::Requirement
|
301
|
+
none: false
|
302
|
+
requirements:
|
303
|
+
- - ~>
|
304
|
+
- !ruby/object:Gem::Version
|
305
|
+
version: 1.4.4
|
306
|
+
type: :development
|
307
|
+
prerelease: false
|
308
|
+
version_requirements: !ruby/object:Gem::Requirement
|
309
|
+
none: false
|
310
|
+
requirements:
|
311
|
+
- - ~>
|
312
|
+
- !ruby/object:Gem::Version
|
313
|
+
version: 1.4.4
|
314
|
+
- !ruby/object:Gem::Dependency
|
315
|
+
name: rails
|
316
|
+
requirement: !ruby/object:Gem::Requirement
|
317
|
+
none: false
|
318
|
+
requirements:
|
319
|
+
- - '='
|
320
|
+
- !ruby/object:Gem::Version
|
321
|
+
version: 3.2.13
|
322
|
+
type: :development
|
323
|
+
prerelease: false
|
324
|
+
version_requirements: !ruby/object:Gem::Requirement
|
325
|
+
none: false
|
326
|
+
requirements:
|
327
|
+
- - '='
|
328
|
+
- !ruby/object:Gem::Version
|
329
|
+
version: 3.2.13
|
330
|
+
description: Shared models for Panopticon and Publisher
|
331
|
+
email:
|
332
|
+
- pbattley@gmail.com
|
333
|
+
executables: []
|
334
|
+
extensions: []
|
335
|
+
extra_rdoc_files: []
|
336
|
+
files:
|
337
|
+
- .gitignore
|
338
|
+
- .ruby-version
|
339
|
+
- .travis.yml
|
340
|
+
- CONTRIBUTING.md
|
341
|
+
- Gemfile
|
342
|
+
- LICENSE
|
343
|
+
- README.md
|
344
|
+
- Rakefile
|
345
|
+
- app/models/action.rb
|
346
|
+
- app/models/answer_edition.rb
|
347
|
+
- app/models/artefact.rb
|
348
|
+
- app/models/artefact_action.rb
|
349
|
+
- app/models/artefact_external_link.rb
|
350
|
+
- app/models/business_support/business_size.rb
|
351
|
+
- app/models/business_support/business_type.rb
|
352
|
+
- app/models/business_support/location.rb
|
353
|
+
- app/models/business_support/purpose.rb
|
354
|
+
- app/models/business_support/sector.rb
|
355
|
+
- app/models/business_support/stage.rb
|
356
|
+
- app/models/business_support/support_type.rb
|
357
|
+
- app/models/business_support_edition.rb
|
358
|
+
- app/models/campaign_edition.rb
|
359
|
+
- app/models/completed_transaction_edition.rb
|
360
|
+
- app/models/curated_list.rb
|
361
|
+
- app/models/edition.rb
|
362
|
+
- app/models/expectant.rb
|
363
|
+
- app/models/expectation.rb
|
364
|
+
- app/models/guide_edition.rb
|
365
|
+
- app/models/help_page_edition.rb
|
366
|
+
- app/models/licence_edition.rb
|
367
|
+
- app/models/local_authority.rb
|
368
|
+
- app/models/local_interaction.rb
|
369
|
+
- app/models/local_service.rb
|
370
|
+
- app/models/local_transaction_edition.rb
|
371
|
+
- app/models/overview_dashboard.rb
|
372
|
+
- app/models/part.rb
|
373
|
+
- app/models/parted.rb
|
374
|
+
- app/models/place_edition.rb
|
375
|
+
- app/models/programme_edition.rb
|
376
|
+
- app/models/simple_smart_answer_edition.rb
|
377
|
+
- app/models/simple_smart_answer_edition/node.rb
|
378
|
+
- app/models/simple_smart_answer_edition/node/option.rb
|
379
|
+
- app/models/tag.rb
|
380
|
+
- app/models/transaction_edition.rb
|
381
|
+
- app/models/travel_advice_edition.rb
|
382
|
+
- app/models/user.rb
|
383
|
+
- app/models/video_edition.rb
|
384
|
+
- app/models/workflow.rb
|
385
|
+
- app/models/workflow_actor.rb
|
386
|
+
- app/traits/attachable.rb
|
387
|
+
- app/traits/govspeak_smart_quotes_fixer.rb
|
388
|
+
- app/traits/taggable.rb
|
389
|
+
- app/validators/safe_html.rb
|
390
|
+
- app/validators/slug_validator.rb
|
391
|
+
- config/mongoid.yml
|
392
|
+
- govuk_content_models.gemspec
|
393
|
+
- jenkins.sh
|
394
|
+
- lib/fact_check_address.rb
|
395
|
+
- lib/govuk_content_models.rb
|
396
|
+
- lib/govuk_content_models/require_all.rb
|
397
|
+
- lib/govuk_content_models/test_helpers/factories.rb
|
398
|
+
- lib/govuk_content_models/test_helpers/local_services.rb
|
399
|
+
- lib/govuk_content_models/version.rb
|
400
|
+
- test/fixtures/contactotron_api_response.json
|
401
|
+
- test/fixtures/uploads/image.jpg
|
402
|
+
- test/models/artefact_action_test.rb
|
403
|
+
- test/models/artefact_external_link_test.rb
|
404
|
+
- test/models/artefact_tag_test.rb
|
405
|
+
- test/models/artefact_test.rb
|
406
|
+
- test/models/business_support/business_size_test.rb
|
407
|
+
- test/models/business_support/business_type_test.rb
|
408
|
+
- test/models/business_support/location_test.rb
|
409
|
+
- test/models/business_support/purpose_test.rb
|
410
|
+
- test/models/business_support/sector_test.rb
|
411
|
+
- test/models/business_support/stage_test.rb
|
412
|
+
- test/models/business_support/support_type_test.rb
|
413
|
+
- test/models/business_support_edition_test.rb
|
414
|
+
- test/models/campaign_edition_test.rb
|
415
|
+
- test/models/curated_list_test.rb
|
416
|
+
- test/models/edition_test.rb
|
417
|
+
- test/models/fact_check_address_test.rb
|
418
|
+
- test/models/help_page_edition_test.rb
|
419
|
+
- test/models/licence_edition_test.rb
|
420
|
+
- test/models/local_authority_test.rb
|
421
|
+
- test/models/local_service_test.rb
|
422
|
+
- test/models/local_transaction_edition_test.rb
|
423
|
+
- test/models/overview_dashboard_test.rb
|
424
|
+
- test/models/simple_smart_answer_edition_test.rb
|
425
|
+
- test/models/simple_smart_answer_node_test.rb
|
426
|
+
- test/models/simple_smart_answer_option_test.rb
|
427
|
+
- test/models/tag_test.rb
|
428
|
+
- test/models/time_zone_test.rb
|
429
|
+
- test/models/transaction_edition_test.rb
|
430
|
+
- test/models/travel_advice_edition_test.rb
|
431
|
+
- test/models/user_test.rb
|
432
|
+
- test/models/video_edition_test.rb
|
433
|
+
- test/models/workflow_actor_test.rb
|
434
|
+
- test/models/workflow_test.rb
|
435
|
+
- test/test_helper.rb
|
436
|
+
- test/traits/attachable_test.rb
|
437
|
+
- test/traits/taggable_test.rb
|
438
|
+
- test/validators/safe_html_validator_test.rb
|
439
|
+
- test/validators/slug_validator_test.rb
|
440
|
+
homepage: https://github.com/alphagov/govuk_content_models
|
441
|
+
licenses: []
|
442
|
+
post_install_message:
|
443
|
+
rdoc_options: []
|
444
|
+
require_paths:
|
445
|
+
- lib
|
446
|
+
- app
|
447
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
448
|
+
none: false
|
449
|
+
requirements:
|
450
|
+
- - ! '>='
|
451
|
+
- !ruby/object:Gem::Version
|
452
|
+
version: '0'
|
453
|
+
segments:
|
454
|
+
- 0
|
455
|
+
hash: 657886085412273794
|
456
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
457
|
+
none: false
|
458
|
+
requirements:
|
459
|
+
- - ! '>='
|
460
|
+
- !ruby/object:Gem::Version
|
461
|
+
version: '0'
|
462
|
+
segments:
|
463
|
+
- 0
|
464
|
+
hash: 657886085412273794
|
465
|
+
requirements: []
|
466
|
+
rubyforge_project:
|
467
|
+
rubygems_version: 1.8.23
|
468
|
+
signing_key:
|
469
|
+
specification_version: 3
|
470
|
+
summary: Shared models for Panopticon and Publisher, as a Rails Engine
|
471
|
+
test_files:
|
472
|
+
- test/fixtures/contactotron_api_response.json
|
473
|
+
- test/fixtures/uploads/image.jpg
|
474
|
+
- test/models/artefact_action_test.rb
|
475
|
+
- test/models/artefact_external_link_test.rb
|
476
|
+
- test/models/artefact_tag_test.rb
|
477
|
+
- test/models/artefact_test.rb
|
478
|
+
- test/models/business_support/business_size_test.rb
|
479
|
+
- test/models/business_support/business_type_test.rb
|
480
|
+
- test/models/business_support/location_test.rb
|
481
|
+
- test/models/business_support/purpose_test.rb
|
482
|
+
- test/models/business_support/sector_test.rb
|
483
|
+
- test/models/business_support/stage_test.rb
|
484
|
+
- test/models/business_support/support_type_test.rb
|
485
|
+
- test/models/business_support_edition_test.rb
|
486
|
+
- test/models/campaign_edition_test.rb
|
487
|
+
- test/models/curated_list_test.rb
|
488
|
+
- test/models/edition_test.rb
|
489
|
+
- test/models/fact_check_address_test.rb
|
490
|
+
- test/models/help_page_edition_test.rb
|
491
|
+
- test/models/licence_edition_test.rb
|
492
|
+
- test/models/local_authority_test.rb
|
493
|
+
- test/models/local_service_test.rb
|
494
|
+
- test/models/local_transaction_edition_test.rb
|
495
|
+
- test/models/overview_dashboard_test.rb
|
496
|
+
- test/models/simple_smart_answer_edition_test.rb
|
497
|
+
- test/models/simple_smart_answer_node_test.rb
|
498
|
+
- test/models/simple_smart_answer_option_test.rb
|
499
|
+
- test/models/tag_test.rb
|
500
|
+
- test/models/time_zone_test.rb
|
501
|
+
- test/models/transaction_edition_test.rb
|
502
|
+
- test/models/travel_advice_edition_test.rb
|
503
|
+
- test/models/user_test.rb
|
504
|
+
- test/models/video_edition_test.rb
|
505
|
+
- test/models/workflow_actor_test.rb
|
506
|
+
- test/models/workflow_test.rb
|
507
|
+
- test/test_helper.rb
|
508
|
+
- test/traits/attachable_test.rb
|
509
|
+
- test/traits/taggable_test.rb
|
510
|
+
- test/validators/safe_html_validator_test.rb
|
511
|
+
- test/validators/slug_validator_test.rb
|