releaf-content 0.2.1
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.
- checksums.yaml +7 -0
- data/LICENSE +24 -0
- data/app/assets/javascripts/releaf/controllers/releaf/content/nodes.js +88 -0
- data/app/assets/stylesheets/releaf/controllers/releaf/content/nodes.scss +234 -0
- data/app/builders/releaf/content/builders/action_dialog.rb +60 -0
- data/app/builders/releaf/content/builders/dialog.rb +15 -0
- data/app/builders/releaf/content/builders/tree.rb +84 -0
- data/app/builders/releaf/content/content_type_dialog_builder.rb +74 -0
- data/app/builders/releaf/content/copy_dialog_builder.rb +9 -0
- data/app/builders/releaf/content/go_to_dialog_builder.rb +9 -0
- data/app/builders/releaf/content/move_dialog_builder.rb +9 -0
- data/app/builders/releaf/content/nodes/content_form_builder.rb +7 -0
- data/app/builders/releaf/content/nodes/form_builder.rb +108 -0
- data/app/builders/releaf/content/nodes/index_builder.rb +24 -0
- data/app/builders/releaf/content/nodes/toolbox_builder.rb +33 -0
- data/app/controllers/releaf/content/nodes_controller.rb +166 -0
- data/app/middleware/releaf/content/routes_reloader.rb +25 -0
- data/app/validators/releaf/content/node/parent_validator.rb +48 -0
- data/app/validators/releaf/content/node/root_validator.rb +43 -0
- data/app/validators/releaf/content/node/singleness_validator.rb +102 -0
- data/app/views/releaf/content/nodes/content_type_dialog.ruby +1 -0
- data/app/views/releaf/content/nodes/copy_dialog.ruby +1 -0
- data/app/views/releaf/content/nodes/go_to_dialog.ruby +1 -0
- data/app/views/releaf/content/nodes/move_dialog.ruby +1 -0
- data/lib/releaf-content.rb +6 -0
- data/lib/releaf/content/acts_as_node.rb +73 -0
- data/lib/releaf/content/acts_as_node/action_controller/acts/node.rb +17 -0
- data/lib/releaf/content/acts_as_node/active_record/acts/node.rb +55 -0
- data/lib/releaf/content/builders_autoload.rb +18 -0
- data/lib/releaf/content/engine.rb +40 -0
- data/lib/releaf/content/node.rb +280 -0
- data/lib/releaf/content/node_mapper.rb +9 -0
- data/lib/releaf/content/route.rb +93 -0
- data/lib/releaf/content/router_proxy.rb +23 -0
- data/releaf-content.gemspec +20 -0
- data/spec/builders/content/nodes/content_form_builder_spec.rb +24 -0
- data/spec/builders/content/nodes/form_builder_spec.rb +218 -0
- data/spec/builders/content/nodes/toolbox_builder_spec.rb +108 -0
- data/spec/controllers/releaf/content/nodes_controller_spec.rb +21 -0
- data/spec/features/nodes_spec.rb +239 -0
- data/spec/lib/releaf/content/acts_as_node_spec.rb +118 -0
- data/spec/lib/releaf/content/node_spec.rb +779 -0
- data/spec/lib/releaf/content/route_spec.rb +85 -0
- data/spec/middleware/routes_reloader_spec.rb +48 -0
- data/spec/routing/node_mapper_spec.rb +142 -0
- data/spec/validators/content/node/parent_validator_spec.rb +56 -0
- data/spec/validators/content/node/root_validator_spec.rb +69 -0
- data/spec/validators/content/node/singleness_validator_spec.rb +145 -0
- metadata +145 -0
@@ -0,0 +1,85 @@
|
|
1
|
+
require "rails_helper"
|
2
|
+
|
3
|
+
describe Releaf::Content::Route do
|
4
|
+
let(:node_route) { FactoryGirl.build(:node_route, node_id: 12, locale: "en", path: "/en") }
|
5
|
+
|
6
|
+
describe ".node_class" do
|
7
|
+
it "returns ::Node" do
|
8
|
+
expect( described_class.node_class ).to eq ::Node
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
describe ".node_class_default_controller" do
|
13
|
+
context "when given node class inherits `ActionController::Base`" do
|
14
|
+
it "returns undercored, stripped down controller class" do
|
15
|
+
expect(described_class.node_class_default_controller(HomePagesController)).to eq("home_pages")
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
context "when given node class does not inherit `ActionController::Base`" do
|
20
|
+
it "returns pluralized, underscorized class" do
|
21
|
+
expect(described_class.node_class_default_controller(TextPage)).to eq("text_pages")
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
describe ".for" do
|
27
|
+
before do
|
28
|
+
create(:home_page_node)
|
29
|
+
end
|
30
|
+
|
31
|
+
it "returns an array" do
|
32
|
+
expect(described_class.for(HomePage, 'foo').class).to eq(Array)
|
33
|
+
end
|
34
|
+
|
35
|
+
context "when databse doesn't exists" do
|
36
|
+
it "returns an empty array" do
|
37
|
+
allow(described_class.node_class).to receive(:where).and_raise(ActiveRecord::NoDatabaseError.new("xxx"))
|
38
|
+
expect(described_class.for(HomePage, 'foo')).to eq([])
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
context "when releaf_nodes table doesn't exists" do
|
43
|
+
it "returns an empty array" do
|
44
|
+
allow(described_class.node_class).to receive(:where).and_raise(ActiveRecord::StatementInvalid.new("xxx"))
|
45
|
+
expect(described_class.for(HomePage, 'foo')).to eq([])
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
context "when releaf_nodes table exists" do
|
50
|
+
it "returns an array of Node::Route objects" do
|
51
|
+
result = described_class.for(HomePage, 'foo')
|
52
|
+
expect(result.count).to eq(1)
|
53
|
+
expect(result.first.class).to eq(described_class)
|
54
|
+
end
|
55
|
+
|
56
|
+
context "when node is not available" do
|
57
|
+
it "does not include it in return" do
|
58
|
+
allow_any_instance_of(Node).to receive(:available?).and_return(false)
|
59
|
+
expect(described_class.for(HomePage, 'foo')).to eq([])
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
describe '#params' do
|
66
|
+
it "returns params for router method" do
|
67
|
+
expect(node_route.params("home#index")).to eq ['/en', {:node_id=>"12", :locale=>"en", :to=>"home#index"}]
|
68
|
+
end
|
69
|
+
|
70
|
+
context "when :as given in args" do
|
71
|
+
context "when node has a locale" do
|
72
|
+
it "prepends locale to :as" do
|
73
|
+
expect(node_route.params("home#index", as: "home").last).to match hash_including(as: "en_home")
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
context "when node does not have a locale" do
|
78
|
+
it "doesn't modify :as option" do
|
79
|
+
node_route.locale = nil
|
80
|
+
expect(node_route.params("home#index", as: "home").last).to match hash_including(as: "home")
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
require "rails_helper"
|
2
|
+
|
3
|
+
describe Releaf::Content::RoutesReloader do
|
4
|
+
let(:app) { ->(env) { [200, env, "app"] } }
|
5
|
+
|
6
|
+
let :request do
|
7
|
+
described_class.new(app).call(Rack::MockRequest.env_for('http://example.com'))
|
8
|
+
end
|
9
|
+
|
10
|
+
describe "on application startup" do
|
11
|
+
it "sets routes loading time" do
|
12
|
+
expect(described_class.routes_loaded).to_not be_nil
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
describe "on each request" do
|
17
|
+
it "compares latest updates" do
|
18
|
+
expect(described_class).to receive(:reload_if_expired)
|
19
|
+
request
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
describe ".reload_if_expired" do
|
24
|
+
context "when no nodes exists" do
|
25
|
+
it "does not reload routes" do
|
26
|
+
allow(Node).to receive(:updated_at).and_return(nil)
|
27
|
+
expect(Rails.application).to_not receive(:reload_routes!)
|
28
|
+
described_class.reload_if_expired
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
context "when routes is up to date" do
|
33
|
+
it "does not reload routes" do
|
34
|
+
allow(Node).to receive(:updated_at).and_return(Time.parse("1991-01-01"))
|
35
|
+
expect(Rails.application).to_not receive(:reload_routes!)
|
36
|
+
described_class.reload_if_expired
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
context "when routes is outdated" do
|
41
|
+
it "reloads routes" do
|
42
|
+
allow(Node).to receive(:updated_at).and_return(Time.now)
|
43
|
+
expect(Rails.application).to receive(:reload_routes!)
|
44
|
+
described_class.reload_if_expired
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,142 @@
|
|
1
|
+
require "rails_helper"
|
2
|
+
|
3
|
+
describe Releaf::Content::NodeMapper do
|
4
|
+
after(:all) do
|
5
|
+
# reset dummy app routes
|
6
|
+
Dummy::Application.reload_routes!
|
7
|
+
end
|
8
|
+
|
9
|
+
before do
|
10
|
+
Dummy::Application.reload_routes!
|
11
|
+
@text_page = FactoryGirl.create(:text_page)
|
12
|
+
@lv_root = create(:home_page_node, name: "lv", locale: "lv", slug: 'lv')
|
13
|
+
@node = FactoryGirl.create(:node, slug: 'test-page', content: @text_page, parent: @lv_root)
|
14
|
+
end
|
15
|
+
|
16
|
+
describe "#releaf_routes_for" do
|
17
|
+
|
18
|
+
describe "using current node path" do
|
19
|
+
example "using default controller " do
|
20
|
+
routes.draw do
|
21
|
+
releaf_routes_for(TextPage) do |route|
|
22
|
+
get 'show'
|
23
|
+
delete :destroy
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
expect(get: '/lv/test-page').to route_to(
|
28
|
+
"controller" => "text_pages",
|
29
|
+
"action" => "show",
|
30
|
+
"node_id" => @node.id.to_s,
|
31
|
+
"locale" => 'lv'
|
32
|
+
)
|
33
|
+
|
34
|
+
expect(delete: '/lv/test-page').to route_to(
|
35
|
+
"controller" => "text_pages",
|
36
|
+
"action" => "destroy",
|
37
|
+
"node_id" => @node.id.to_s,
|
38
|
+
"locale" => 'lv'
|
39
|
+
)
|
40
|
+
end
|
41
|
+
|
42
|
+
example "overriding default controller" do
|
43
|
+
routes.draw do
|
44
|
+
releaf_routes_for(TextPage, controller: 'home_pages') do |route|
|
45
|
+
get 'show'
|
46
|
+
delete :destroy
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
expect(get: '/lv/test-page').to route_to(
|
51
|
+
"controller" => "home_pages",
|
52
|
+
"action" => "show",
|
53
|
+
"node_id" => @node.id.to_s,
|
54
|
+
"locale" => 'lv'
|
55
|
+
)
|
56
|
+
end
|
57
|
+
|
58
|
+
example "specifying different than default controller for route" do
|
59
|
+
routes.draw do
|
60
|
+
releaf_routes_for(TextPage, controller: 'doesnt_matter') do |route|
|
61
|
+
get 'home_pages#hide'
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
expect(get: '/lv/test-page').to route_to(
|
66
|
+
"controller" => "home_pages",
|
67
|
+
"action" => "hide",
|
68
|
+
"node_id" => @node.id.to_s,
|
69
|
+
"locale" => 'lv'
|
70
|
+
)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
describe "adding uri parts to node path" do
|
75
|
+
example "speciffying custom controller and action" do
|
76
|
+
routes.draw do
|
77
|
+
releaf_routes_for(TextPage) do |route|
|
78
|
+
get ':my_id/list', to: 'home_pages#list'
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
expect(get: '/lv/test-page/8888/list').to route_to(
|
83
|
+
"controller" => "home_pages",
|
84
|
+
"action" => "list",
|
85
|
+
"node_id" => @node.id.to_s,
|
86
|
+
"locale" => 'lv',
|
87
|
+
"my_id" => '8888'
|
88
|
+
)
|
89
|
+
end
|
90
|
+
|
91
|
+
example "speciffying action" do
|
92
|
+
routes.draw do
|
93
|
+
releaf_routes_for(TextPage) do |route|
|
94
|
+
get ':my_id/list', to: '#list'
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
expect(get: '/lv/test-page/8888/list').to route_to(
|
99
|
+
"controller" => "text_pages",
|
100
|
+
"action" => "list",
|
101
|
+
"node_id" => @node.id.to_s,
|
102
|
+
"locale" => 'lv',
|
103
|
+
"my_id" => '8888'
|
104
|
+
)
|
105
|
+
end
|
106
|
+
|
107
|
+
example "speciffying action and overriding default controller" do
|
108
|
+
routes.draw do
|
109
|
+
releaf_routes_for(TextPage, controller: 'home_pages') do |route|
|
110
|
+
get ':my_id/list', to: '#list'
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
expect(get: '/lv/test-page/8888/list').to route_to(
|
115
|
+
"controller" => "home_pages",
|
116
|
+
"action" => "list",
|
117
|
+
"node_id" => @node.id.to_s,
|
118
|
+
"locale" => 'lv',
|
119
|
+
"my_id" => '8888'
|
120
|
+
)
|
121
|
+
end
|
122
|
+
|
123
|
+
example "speciffying custom path" do
|
124
|
+
routes.draw do
|
125
|
+
releaf_routes_for(TextPage) do |route|
|
126
|
+
get 'home_pages#show', path: "#{route.path}/abc/:my_id"
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
expect(get: '/lv/test-page/abc/333').to route_to(
|
131
|
+
"controller" => "home_pages",
|
132
|
+
"action" => "show",
|
133
|
+
"node_id" => @node.id.to_s,
|
134
|
+
"locale" => 'lv',
|
135
|
+
"my_id" => '333'
|
136
|
+
)
|
137
|
+
end
|
138
|
+
|
139
|
+
end
|
140
|
+
|
141
|
+
end
|
142
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
require 'rails_helper'
|
2
|
+
|
3
|
+
describe Releaf::Content::Node::ParentValidator do
|
4
|
+
let!(:root_node) { FactoryGirl.create(:node, content_type: 'HomePage') }
|
5
|
+
|
6
|
+
class DummyNodeParentValidatorModel < ActiveRecord::Base
|
7
|
+
acts_as_node
|
8
|
+
self.table_name = 'texts'
|
9
|
+
end
|
10
|
+
|
11
|
+
class DummyNodeParentValidator1Controller < ActionController::Base
|
12
|
+
acts_as_node
|
13
|
+
end
|
14
|
+
|
15
|
+
class DummyNodeParentValidator2Controller < ActionController::Base
|
16
|
+
acts_as_node
|
17
|
+
end
|
18
|
+
|
19
|
+
|
20
|
+
class DummyNodeParentValidatorNode < ActiveRecord::Base
|
21
|
+
self.table_name = 'nodes'
|
22
|
+
include Releaf::Content::Node
|
23
|
+
validates_with Releaf::Content::Node::ParentValidator, for: DummyNodeParentValidatorModel, under: DummyNodeParentValidator1Controller
|
24
|
+
end
|
25
|
+
|
26
|
+
context "when parent is valid" do
|
27
|
+
it "doesn't add error" do
|
28
|
+
parent = DummyNodeParentValidatorNode.create!( FactoryGirl.attributes_for(:node, content_type: 'DummyNodeParentValidator1Controller') )
|
29
|
+
child = DummyNodeParentValidatorNode.new( FactoryGirl.attributes_for(:node, content_type: 'DummyNodeParentValidatorModel', parent_id: parent.id) )
|
30
|
+
|
31
|
+
expect( child ).to be_valid
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
context "when parent is invalid" do
|
36
|
+
it "adds error on content_type" do
|
37
|
+
parent = DummyNodeParentValidatorNode.create!( FactoryGirl.attributes_for(:node, content_type: 'DummyNodeParentValidator2Controller') )
|
38
|
+
child = DummyNodeParentValidatorNode.new( FactoryGirl.attributes_for(:node, content_type: 'DummyNodeParentValidatorModel', parent_id: parent.id) )
|
39
|
+
|
40
|
+
expect( child ).to be_invalid
|
41
|
+
expect( child.errors[:content_type].size ).to eq(1)
|
42
|
+
expect( child.errors[:content_type] ).to include("invalid parent node")
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
46
|
+
|
47
|
+
context "when content_type is not in child list" do
|
48
|
+
it "doesn't add error" do
|
49
|
+
parent = DummyNodeParentValidatorNode.create!( FactoryGirl.attributes_for(:node, content_type: 'DummyNodeParentValidator1Controller') )
|
50
|
+
child = DummyNodeParentValidatorNode.new( FactoryGirl.attributes_for(:node, content_type: 'DummyNodeParentValidator2Controller', parent_id: parent.id) )
|
51
|
+
|
52
|
+
expect( child ).to be_valid
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
require 'rails_helper'
|
2
|
+
|
3
|
+
describe Releaf::Content::Node::RootValidator do
|
4
|
+
|
5
|
+
|
6
|
+
class DummyRootValidatorController < ActionController::Base
|
7
|
+
acts_as_node
|
8
|
+
end
|
9
|
+
|
10
|
+
class DummyRootValidator2Controller < ActionController::Base
|
11
|
+
acts_as_node
|
12
|
+
end
|
13
|
+
|
14
|
+
class DummyRootValidatorNode < ActiveRecord::Base
|
15
|
+
self.table_name = 'nodes'
|
16
|
+
include Releaf::Content::Node
|
17
|
+
validates_with Releaf::Content::Node::RootValidator, allow: DummyRootValidatorController
|
18
|
+
end
|
19
|
+
|
20
|
+
|
21
|
+
def create_node *params
|
22
|
+
DummyRootValidatorNode.create!( FactoryGirl.attributes_for(:node, *params) )
|
23
|
+
end
|
24
|
+
|
25
|
+
def build_node *params
|
26
|
+
DummyRootValidatorNode.new( FactoryGirl.attributes_for(:node, *params) )
|
27
|
+
end
|
28
|
+
|
29
|
+
context "when node is allowed to be root node" do
|
30
|
+
context "when node is a root node" do
|
31
|
+
it "doesn't add an error" do
|
32
|
+
root_node = build_node(content_type: 'DummyRootValidatorController')
|
33
|
+
expect( root_node ).to be_valid
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
context "when node is not a root node" do
|
38
|
+
it "adds an error" do
|
39
|
+
root_node = create_node(content_type: 'DummyRootValidatorController')
|
40
|
+
subnode = build_node(content_type: 'DummyRootValidatorController', parent: root_node)
|
41
|
+
expect( subnode ).to be_invalid
|
42
|
+
expect( subnode.errors[:content_type].size ).to eq(1)
|
43
|
+
expect( subnode.errors[:content_type] ).to include("can't be subnode")
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
context "when node is not allowed to be a root node" do
|
49
|
+
context "when node is a root node" do
|
50
|
+
it "adds an error" do
|
51
|
+
root_node = build_node(content_type: 'DummyRootValidator2Controller')
|
52
|
+
expect( root_node ).to be_invalid
|
53
|
+
expect( root_node.errors[:content_type].size ).to eq(1)
|
54
|
+
expect( root_node.errors[:content_type] ).to include("can't be root node")
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
context "when node is not a root node" do
|
59
|
+
it "doesn't add an error" do
|
60
|
+
root_node = create_node(content_type: 'DummyRootValidatorController')
|
61
|
+
subnode = build_node(content_type: 'DummyRootValidator2Controller', parent: root_node)
|
62
|
+
|
63
|
+
expect( subnode ).to be_valid
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
|
69
|
+
end
|
@@ -0,0 +1,145 @@
|
|
1
|
+
require 'rails_helper'
|
2
|
+
|
3
|
+
describe Releaf::Content::Node::SinglenessValidator do
|
4
|
+
|
5
|
+
|
6
|
+
class DummySinglenessValidatorModel < ActiveRecord::Base
|
7
|
+
acts_as_node
|
8
|
+
self.table_name = 'text_pages'
|
9
|
+
end
|
10
|
+
|
11
|
+
class DummySinglenessValidator2Model < ActiveRecord::Base
|
12
|
+
acts_as_node
|
13
|
+
self.table_name = 'text_pages'
|
14
|
+
end
|
15
|
+
|
16
|
+
class DummySinglenessValidatorController < ActionController::Base
|
17
|
+
acts_as_node
|
18
|
+
end
|
19
|
+
|
20
|
+
class DummySinglenessValidator2Controller < ActionController::Base
|
21
|
+
acts_as_node
|
22
|
+
end
|
23
|
+
|
24
|
+
class DummySinglenessValidatorNode < ActiveRecord::Base
|
25
|
+
self.table_name = 'nodes'
|
26
|
+
include Releaf::Content::Node
|
27
|
+
validates_with Releaf::Content::Node::SinglenessValidator, for: DummySinglenessValidatorModel
|
28
|
+
validates_with Releaf::Content::Node::SinglenessValidator, for: DummySinglenessValidator2Controller, under: TextPage
|
29
|
+
end
|
30
|
+
|
31
|
+
|
32
|
+
def create_node *params
|
33
|
+
DummySinglenessValidatorNode.create!( FactoryGirl.attributes_for(:node, *params) )
|
34
|
+
end
|
35
|
+
|
36
|
+
def build_node *params
|
37
|
+
DummySinglenessValidatorNode.new( FactoryGirl.attributes_for(:node, *params) )
|
38
|
+
end
|
39
|
+
|
40
|
+
let!(:root_node) { create_node(content_type: 'HomePage') }
|
41
|
+
|
42
|
+
|
43
|
+
context "when scope is entire page" do
|
44
|
+
|
45
|
+
context "When node not mentioned in list" do
|
46
|
+
it "doesn't add error" do
|
47
|
+
node = build_node(content_type: 'DummySinglenessValidatorController', parent_id: root_node.id)
|
48
|
+
expect( node ).to be_valid
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
|
53
|
+
context "when node with given content doesn't exist in tree" do
|
54
|
+
it "doesn't add error" do
|
55
|
+
node = build_node(content_type: 'DummySinglenessValidatorModel', parent_id: root_node.id)
|
56
|
+
expect( node ).to be_valid
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
context "when node with given content exists in tree" do
|
61
|
+
before do
|
62
|
+
create_node(content_type: 'DummySinglenessValidatorModel', parent_id: root_node.id)
|
63
|
+
end
|
64
|
+
|
65
|
+
it "adds error to #content_type" do
|
66
|
+
node = build_node(content_type: 'DummySinglenessValidatorModel', parent_id: root_node.id)
|
67
|
+
expect( node ).to be_invalid
|
68
|
+
expect( node.errors[:content_type].size ).to eq(1)
|
69
|
+
expect( node.errors[:content_type] ).to include("node exists")
|
70
|
+
end
|
71
|
+
|
72
|
+
end
|
73
|
+
|
74
|
+
context "when node is saved, and is only one in the tree" do
|
75
|
+
it "doesn't add error" do
|
76
|
+
node = create_node(content_type: 'DummySinglenessValidatorModel', parent_id: root_node.id)
|
77
|
+
expect( node ).to be_valid
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
context "when scope is subtree" do
|
83
|
+
context "when has ancestor in :under list" do
|
84
|
+
let!(:grand_parent_node) { create_node(content_type: 'TextPage', parent_id: root_node.id) }
|
85
|
+
let!(:parent_node) { create_node(content_type: 'DummySinglenessValidator2Model', parent_id: grand_parent_node.id) }
|
86
|
+
|
87
|
+
context "when node with given content doesn't exist in subtree" do
|
88
|
+
it "doesn't add error" do
|
89
|
+
node = build_node(content_type: 'DummySinglenessValidator2Controller', parent_id: parent_node.id)
|
90
|
+
expect( node ).to be_valid
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
context "when node with given content exists in subtree" do
|
95
|
+
before do
|
96
|
+
create_node(content_type: 'DummySinglenessValidator2Controller', parent_id: grand_parent_node.id)
|
97
|
+
end
|
98
|
+
|
99
|
+
it "adds error to #content_type" do
|
100
|
+
node = build_node(content_type: 'DummySinglenessValidator2Controller', parent_id: parent_node.id)
|
101
|
+
expect( node ).to be_invalid
|
102
|
+
expect( node.errors[:content_type].size ).to eq(1)
|
103
|
+
expect( node.errors[:content_type] ).to include("node exists")
|
104
|
+
end
|
105
|
+
|
106
|
+
end
|
107
|
+
|
108
|
+
context "when node is saved, and is only one in subtree" do
|
109
|
+
it "doesn't add error" do
|
110
|
+
node = create_node(content_type: 'DummySinglenessValidator2Controller', parent_id: parent_node.id)
|
111
|
+
expect( node ).to be_valid
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
|
116
|
+
end
|
117
|
+
|
118
|
+
context "when node has no ancestor in :under list" do
|
119
|
+
it "doesn't add error" do
|
120
|
+
node = create_node(content_type: 'DummySinglenessValidator2Controller', parent_id: root_node.id)
|
121
|
+
expect( node ).to be_valid
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
context "regression tests" do
|
127
|
+
context "@node.parent.self_and_ancestors bug" do
|
128
|
+
it "works correctly / is worked around" do
|
129
|
+
# for details see Releaf::Content::Node::SinglenessValidator#base_relation_for_subtree
|
130
|
+
@node1 = create_node(content_type: 'TextPage', locale: 'en')
|
131
|
+
@node2 = create_node(content_type: 'TextPage', locale: 'lv')
|
132
|
+
@node3 = create_node(content_type: 'TextPage', locale: 'ru')
|
133
|
+
@node4 = create_node(content_type: 'TextPage', locale: 'sp')
|
134
|
+
|
135
|
+
@node1_1 = create_node(content_type: 'DummySinglenessValidator2Controller', parent: @node1)
|
136
|
+
expect do
|
137
|
+
@node1_2 = create_node(content_type: 'DummySinglenessValidator2Controller', parent: @node2)
|
138
|
+
@node1_3 = create_node(content_type: 'DummySinglenessValidator2Controller', parent: @node3)
|
139
|
+
@node1_4 = create_node(content_type: 'DummySinglenessValidator2Controller', parent: @node4)
|
140
|
+
end.to_not raise_error
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
end
|