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,218 @@
|
|
1
|
+
require "rails_helper"
|
2
|
+
|
3
|
+
describe Releaf::Content::Nodes::FormBuilder, type: :class do
|
4
|
+
class FormBuilderTestHelper < ActionView::Base
|
5
|
+
include Releaf::ApplicationHelper
|
6
|
+
include Releaf::ButtonHelper
|
7
|
+
include FontAwesome::Rails::IconHelper
|
8
|
+
def controller_scope_name; end
|
9
|
+
def generate_url_releaf_content_nodes_path(args); end
|
10
|
+
end
|
11
|
+
|
12
|
+
let(:template){ FormBuilderTestHelper.new }
|
13
|
+
let(:object){ Node.new(content_type: "TextPage", slug: "b", id: 2,
|
14
|
+
parent: Node.new(content_type: "TextPage", slug: "a", id: 1)) }
|
15
|
+
let(:subject){ described_class.new(:resource, object, template, {}) }
|
16
|
+
|
17
|
+
describe "#field_names" do
|
18
|
+
it "returns hidden, node and content object fields" do
|
19
|
+
expect(subject.field_names).to eq(["node_fields_block", "content_fields_block"])
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
describe "#node_fields" do
|
24
|
+
it "returns array with renderable node fields" do
|
25
|
+
list = [:parent_id, :name, :content_type, :slug, :item_position, :active, :locale]
|
26
|
+
expect(subject.node_fields).to eq(list)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
describe "#render_node_fields_block" do
|
31
|
+
it "renders node fields" do
|
32
|
+
allow(subject).to receive(:node_fields).and_return([1, 2])
|
33
|
+
allow(subject).to receive(:releaf_fields).with([1, 2]).and_return("x")
|
34
|
+
content = '<div class="section node-fields">x</div>'
|
35
|
+
expect(subject.render_node_fields_block).to eq(content)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
describe "#render_parent_id" do
|
40
|
+
it "renders hidden parent if field for new object" do
|
41
|
+
allow(subject.object).to receive(:new_record?).and_return(true)
|
42
|
+
allow(subject).to receive(:hidden_field).with(:parent_id).and_return("x")
|
43
|
+
expect(subject.render_parent_id).to eq("x")
|
44
|
+
end
|
45
|
+
|
46
|
+
it "renders nothing for existing object" do
|
47
|
+
allow(subject.object).to receive(:new_record?).and_return(false)
|
48
|
+
expect(subject.render_parent_id).to eq(nil)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
describe "#render_content_fields_block?" do
|
53
|
+
before do
|
54
|
+
subject.object.build_content
|
55
|
+
end
|
56
|
+
|
57
|
+
it "returns array of node content object fields" do
|
58
|
+
allow(object.content_class).to receive(:respond_to?).with(:acts_as_node_fields).and_return(true)
|
59
|
+
expect(subject.render_content_fields_block?).to be true
|
60
|
+
end
|
61
|
+
|
62
|
+
context "when object content class do not respond to `acts_as_node_fields`" do
|
63
|
+
it "returns nil" do
|
64
|
+
allow(object.content_class).to receive(:respond_to?).with(:acts_as_node_fields).and_return(false)
|
65
|
+
expect(subject.render_content_fields_block?).to be false
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
describe "#render_content_fields_block" do
|
71
|
+
before do
|
72
|
+
subject.object.build_content
|
73
|
+
end
|
74
|
+
|
75
|
+
it "renders content fields block" do
|
76
|
+
allow(subject).to receive(:content_builder_class).and_return("_b_")
|
77
|
+
allow(subject).to receive(:render_content_fields_block?).and_return(true)
|
78
|
+
subform = described_class.new(:resource, object, template, {})
|
79
|
+
allow(subject).to receive(:fields_for).with(:content, subject.object.content, builder: "_b_").and_yield(subform)
|
80
|
+
allow(subform).to receive(:field_names).and_return([1, 2])
|
81
|
+
allow(subform).to receive(:releaf_fields).with([1, 2]).and_return("yy")
|
82
|
+
content = '<div class="section content-fields">yy</div>'
|
83
|
+
expect(subject.render_content_fields_block).to eq(content)
|
84
|
+
end
|
85
|
+
|
86
|
+
it "casts form fields to array before passign to `releaf_fields`" do
|
87
|
+
allow(subject).to receive(:content_builder_class).and_return("_b_")
|
88
|
+
allow(subject).to receive(:render_content_fields_block?).and_return(true)
|
89
|
+
subform = described_class.new(:resource, object, template, {})
|
90
|
+
allow(subject).to receive(:fields_for).with(:content, subject.object.content, builder: "_b_").and_yield(subform)
|
91
|
+
allow(subform).to receive(:field_names).and_return({a: 1, b: 2})
|
92
|
+
expect(subform).to receive(:releaf_fields).with([[:a, 1], [:b, 2]])
|
93
|
+
subject.render_content_fields_block
|
94
|
+
end
|
95
|
+
|
96
|
+
context "when content have no fields" do
|
97
|
+
it "returns nil" do
|
98
|
+
allow(subject).to receive(:render_content_fields_block?).and_return(false)
|
99
|
+
expect(subject.render_content_fields_block).to be nil
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
describe "#content_builder_class" do
|
105
|
+
it "returns `Releaf::Content::Nodes::ContentFormBuilder`" do
|
106
|
+
expect(subject.content_builder_class).to eq(Releaf::Content::Nodes::ContentFormBuilder)
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
describe "#render_locale" do
|
111
|
+
context "when node node has locale select enabled" do
|
112
|
+
it "renders locale with #render_locale_options" do
|
113
|
+
allow(subject.object).to receive(:locale_selection_enabled?).and_return(true)
|
114
|
+
allow(subject).to receive(:render_locale_options).and_return(a: "b")
|
115
|
+
allow(subject).to receive(:releaf_item_field).with(:locale, options: {a: "b"}).and_return("x")
|
116
|
+
expect(subject.render_locale).to eq("x")
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
context "when node node does not have locale select enabled" do
|
121
|
+
it "renders locale with #render_locale_options" do
|
122
|
+
allow(subject.object).to receive(:locale_selection_enabled?).and_return(false)
|
123
|
+
expect(subject.render_locale).to be nil
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
describe "#render_locale_options" do
|
129
|
+
it "returns :select_options and :include_blank values" do
|
130
|
+
expect(subject.render_locale_options.keys).to eq([:select_options, :include_blank])
|
131
|
+
end
|
132
|
+
|
133
|
+
it ":select_options contains all available locales" do
|
134
|
+
allow(I18n).to receive(:available_locales).and_return([:lt, :et])
|
135
|
+
expect(subject.render_locale_options[:select_options]).to eq([:lt, :et])
|
136
|
+
end
|
137
|
+
|
138
|
+
context "when subject have defined locale" do
|
139
|
+
it ":include_blank is false" do
|
140
|
+
subject.object.locale = :lt
|
141
|
+
expect(subject.render_locale_options[:include_blank]).to be false
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
context "when subject have no locale" do
|
146
|
+
it ":include_blank is true" do
|
147
|
+
subject.object.locale = nil
|
148
|
+
expect(subject.render_locale_options[:include_blank]).to be true
|
149
|
+
end
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
describe "#render_content_type" do
|
154
|
+
it "renders disabled content type field with localized content type value" do
|
155
|
+
options = {disabled: true, value: "Translated content type"}
|
156
|
+
allow(I18n).to receive(:t).with("text_page", scope: "admin.content_types").and_return("Translated content type")
|
157
|
+
allow(subject).to receive(:releaf_text_field).with(:content_type, input: options).and_return("x")
|
158
|
+
expect(subject.render_content_type).to eq("x")
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
describe "#render_slug" do
|
163
|
+
it "renders customized field" do
|
164
|
+
controller = Releaf::BaseController.new
|
165
|
+
allow(subject).to receive(:controller).and_return(controller)
|
166
|
+
allow(subject).to receive(:slug_base_url).and_return("http://localhost/parent")
|
167
|
+
allow(subject).to receive(:url_for).with(controller: "/releaf/content/nodes", action: "generate_url", parent_id: 1, id: 2)
|
168
|
+
.and_return("http://localhost/slug-generation-url")
|
169
|
+
|
170
|
+
content = '
|
171
|
+
<div class="field type-text" data-name="slug">
|
172
|
+
<div class="label-wrap">
|
173
|
+
<label for="resource_slug">Slug</label>
|
174
|
+
</div>
|
175
|
+
<div class="value">
|
176
|
+
<input value="b" class="text" data-generator-url="http://localhost/slug-generation-url" type="text" name="resource[slug]" id="resource_slug" />
|
177
|
+
<button class="button only-icon secondary generate" title="Suggest slug" type="button" autocomplete="off">
|
178
|
+
<i class="fa fa-keyboard-o"></i>
|
179
|
+
</button>
|
180
|
+
<div class="link">
|
181
|
+
<a href="/a/b">http://localhost/parent<span>b</span>/</a>
|
182
|
+
</div>
|
183
|
+
</div>
|
184
|
+
</div>
|
185
|
+
'
|
186
|
+
|
187
|
+
expect(subject.render_slug).to match_html(content)
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
191
|
+
describe "#render_item_position" do
|
192
|
+
it "renders locale with #item_position_options" do
|
193
|
+
allow(subject).to receive(:item_position_options).and_return(a: "b")
|
194
|
+
allow(subject).to receive(:releaf_item_field).with(:item_position, options: {a: "b"}).and_return("x")
|
195
|
+
expect(subject.render_item_position).to eq("x")
|
196
|
+
end
|
197
|
+
end
|
198
|
+
|
199
|
+
describe "#item_position_options" do
|
200
|
+
before do
|
201
|
+
object.item_position = 2
|
202
|
+
allow(subject).to receive(:item_position_select_options).and_return([["a", 1], ["b", 2], ["c", 3]])
|
203
|
+
end
|
204
|
+
|
205
|
+
it "returns :select_options and :include_blank values" do
|
206
|
+
expect(subject.item_position_options.keys).to eq([:include_blank, :select_options])
|
207
|
+
end
|
208
|
+
|
209
|
+
it ":select_options correct select options" do
|
210
|
+
options = '<option value="1">a</option>'
|
211
|
+
options << "\n"
|
212
|
+
options << '<option selected="selected" value="2">b</option>'
|
213
|
+
options << "\n"
|
214
|
+
options << '<option value="3">c</option>'
|
215
|
+
expect(subject.item_position_options[:select_options]).to eq(options)
|
216
|
+
end
|
217
|
+
end
|
218
|
+
end
|
@@ -0,0 +1,108 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe Releaf::Content::Nodes::ToolboxBuilder, type: :class do
|
4
|
+
|
5
|
+
class NodeToolboxBuilderTestHelper < ActionView::Base
|
6
|
+
include Releaf::ButtonHelper
|
7
|
+
include Releaf::ApplicationHelper
|
8
|
+
end
|
9
|
+
|
10
|
+
let(:template){ NodeToolboxBuilderTestHelper.new }
|
11
|
+
subject { described_class.new(template) }
|
12
|
+
|
13
|
+
let(:node){ Node.new(content_type: "TextPage", slug: "a", id: 99) }
|
14
|
+
|
15
|
+
before do
|
16
|
+
allow(subject).to receive(:resource).and_return(node)
|
17
|
+
allow(subject).to receive(:destroy_confirmation_link).and_return(:super_item)
|
18
|
+
allow(subject).to receive(:feature_available?).with(:destroy).and_return true
|
19
|
+
end
|
20
|
+
|
21
|
+
it "extends Releaf::Builders::ToolboxBuilder" do
|
22
|
+
expect(described_class.ancestors).to include Releaf::Builders::ToolboxBuilder
|
23
|
+
end
|
24
|
+
|
25
|
+
describe "#items" do
|
26
|
+
|
27
|
+
before do
|
28
|
+
allow(subject).to receive(:params).and_return({})
|
29
|
+
allow(subject).to receive(:add_child_button).and_return( :add_child_item )
|
30
|
+
allow(subject).to receive(:go_to_button).and_return( :go_to_item )
|
31
|
+
allow(subject).to receive(:copy_button).and_return( :copy_item )
|
32
|
+
allow(subject).to receive(:move_button).and_return( :move_item )
|
33
|
+
end
|
34
|
+
|
35
|
+
context "when applied to a new record" do
|
36
|
+
|
37
|
+
it "returns only items returned by parent class" do
|
38
|
+
allow(node).to receive(:new_record?).and_return true
|
39
|
+
expect(subject.items).to eq([ :super_item ])
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
43
|
+
|
44
|
+
context "when applied to an existing record" do
|
45
|
+
|
46
|
+
before do
|
47
|
+
allow(node).to receive(:new_record?).and_return false
|
48
|
+
end
|
49
|
+
|
50
|
+
it "prepends add child, go to, copy and move items to the list returned by parent class" do
|
51
|
+
expect(subject.items).to eq([ :add_child_item, :go_to_item, :copy_item, :move_item, :super_item ])
|
52
|
+
end
|
53
|
+
|
54
|
+
context "when in index context" do
|
55
|
+
|
56
|
+
it "does not include go_to_item" do
|
57
|
+
allow(subject).to receive(:params).and_return({ context: "index" })
|
58
|
+
expect(subject.items).to eq([ :add_child_item, :copy_item, :move_item, :super_item ])
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
end
|
63
|
+
|
64
|
+
end
|
65
|
+
|
66
|
+
describe "item methods" do
|
67
|
+
|
68
|
+
describe "#add_child_button" do
|
69
|
+
it "returns an ajaxbox link to content type dialog" do
|
70
|
+
allow(subject).to receive(:t).with('Add child').and_return('addchildxx')
|
71
|
+
allow(subject).to receive(:url_for).with(action: 'content_type_dialog', parent_id: 99).and_return('dialogurl')
|
72
|
+
html = '<a class="button ajaxbox" title="addchildxx" href="dialogurl">addchildxx</a>'
|
73
|
+
expect(subject.add_child_button).to eq(html)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
describe "#go_to_button" do
|
78
|
+
it "returns an ajaxbox link to go to dialog" do
|
79
|
+
allow(subject).to receive(:t).with('Go to').and_return('gotoxx')
|
80
|
+
allow(subject).to receive(:url_for).with(action: 'go_to_dialog').and_return('dialogurl')
|
81
|
+
html = '<a class="button ajaxbox" title="gotoxx" href="dialogurl">gotoxx</a>'
|
82
|
+
expect(subject.go_to_button).to eq(html)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
|
87
|
+
describe "#copy_button" do
|
88
|
+
it "returns an ajaxbox link to copy dialog" do
|
89
|
+
allow(subject).to receive(:t).with('Copy').and_return('copyxx')
|
90
|
+
allow(subject).to receive(:url_for).with(action: 'copy_dialog', id: 99).and_return('dialogurl')
|
91
|
+
html = '<a class="button ajaxbox" title="copyxx" href="dialogurl">copyxx</a>'
|
92
|
+
expect(subject.copy_button).to eq(html)
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
|
97
|
+
describe "#move_button" do
|
98
|
+
it "returns an ajaxbox link to move dialog" do
|
99
|
+
allow(subject).to receive(:t).with('Move').and_return('movexx')
|
100
|
+
allow(subject).to receive(:url_for).with(action: 'move_dialog', id: 99).and_return('dialogurl')
|
101
|
+
html = '<a class="button ajaxbox" title="movexx" href="dialogurl">movexx</a>'
|
102
|
+
expect(subject.move_button).to eq(html)
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
end
|
107
|
+
|
108
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'rails_helper'
|
2
|
+
|
3
|
+
describe Releaf::Content::NodesController do
|
4
|
+
describe "#builder_scopes" do
|
5
|
+
it "adds node builder scope as first scope before default builder scopes" do
|
6
|
+
allow(subject).to receive(:application_scope).and_return("Admin")
|
7
|
+
allow(subject).to receive(:node_builder_scope).and_return("xx")
|
8
|
+
expect(subject.builder_scopes).to eq(["xx", "Releaf::Content::Nodes", "Admin::Builders"])
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
describe "#node_builder_scope" do
|
13
|
+
it "returns node builder scope within releaf mount location scope" do
|
14
|
+
allow(subject).to receive(:application_scope).and_return("Admin")
|
15
|
+
expect(subject.node_builder_scope).to eq("Admin::Nodes")
|
16
|
+
|
17
|
+
allow(subject).to receive(:application_scope).and_return(nil)
|
18
|
+
expect(subject.node_builder_scope).to eq("Nodes")
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,239 @@
|
|
1
|
+
require 'rails_helper'
|
2
|
+
describe "Nodes", js: true, with_tree: true, with_root: true do
|
3
|
+
before do
|
4
|
+
Rails.cache.clear
|
5
|
+
# preload ActsAsNode classes
|
6
|
+
Rails.application.eager_load!
|
7
|
+
@user = auth_as_user
|
8
|
+
end
|
9
|
+
|
10
|
+
before with_root: true do
|
11
|
+
@lv_root = create(:home_page_node, name: "lv", locale: "lv")
|
12
|
+
end
|
13
|
+
|
14
|
+
before with_tree: true do
|
15
|
+
@how_to = create(:text_page_node, parent_id: @lv_root.id)
|
16
|
+
@about_us = create(:text_page_node, parent_id: @lv_root.id, name: "about us")
|
17
|
+
@history_node = create(:text_page_node, parent_id: @about_us.id, name: "history")
|
18
|
+
|
19
|
+
@en_root = create(:home_page_node, name: "en", locale: "en")
|
20
|
+
|
21
|
+
visit releaf_content_nodes_path
|
22
|
+
end
|
23
|
+
|
24
|
+
describe "new node" do
|
25
|
+
|
26
|
+
context "when creating node under root" do
|
27
|
+
it "creates new node in content tree" do
|
28
|
+
@en_root.destroy
|
29
|
+
click_link "Create new resource"
|
30
|
+
click_link "Home page"
|
31
|
+
expect(page).to have_css('.button', text: 'Save')
|
32
|
+
expect(page).to have_no_css('.button', text: 'Save and create another')
|
33
|
+
create_resource do
|
34
|
+
fill_in "resource_name", with: "en"
|
35
|
+
select "en", from: "Locale"
|
36
|
+
end
|
37
|
+
|
38
|
+
expect(page).to have_breadcrumbs("Releaf/content/nodes", "en")
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
context "when creating node under another node" do
|
43
|
+
it "creates new node" do
|
44
|
+
find('li[data-id="' + @lv_root.id.to_s + '"] > .toolbox-cell button').click
|
45
|
+
click_link "Add child"
|
46
|
+
click_link "Contacts controller"
|
47
|
+
expect(page).to have_css('.button', text: 'Save')
|
48
|
+
expect(page).to have_no_css('.button', text: 'Save and create another')
|
49
|
+
create_resource do
|
50
|
+
fill_in "resource_name", with: "Contacts"
|
51
|
+
end
|
52
|
+
|
53
|
+
expect(page).to have_breadcrumbs("Releaf/content/nodes", "lv", "Contacts")
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
describe "tree collapsing" do
|
59
|
+
context "when not opened before" do
|
60
|
+
it "does not show node's children" do
|
61
|
+
expect(page).to have_css('li[data-id="' + @lv_root.id.to_s + '"].collapsed')
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
context "when clicked to uncollapse node" do
|
66
|
+
it "shows node children" do
|
67
|
+
find('li[data-id="' + @lv_root.id.to_s + '"] > .collapser-cell button').click
|
68
|
+
|
69
|
+
expect(page).to have_css('li[data-id="' + @lv_root.id.to_s + '"]:not(.collapsed)')
|
70
|
+
end
|
71
|
+
|
72
|
+
it "keeps opened node children visibility permanent" do
|
73
|
+
find('li[data-id="' + @lv_root.id.to_s + '"] > .collapser-cell button').click
|
74
|
+
wait_for_settings_update("content.tree.expanded.#{@lv_root.id}")
|
75
|
+
visit releaf_content_nodes_path
|
76
|
+
|
77
|
+
expect(page).to have_css('li[data-id="' + @lv_root.id.to_s + '"]:not(.collapsed)')
|
78
|
+
end
|
79
|
+
|
80
|
+
it "keeps closed node children visibility permanent" do
|
81
|
+
find('li[data-id="' + @lv_root.id.to_s + '"] > .collapser-cell button').click
|
82
|
+
find('li[data-id="' + @lv_root.id.to_s + '"] > .collapser-cell button').click
|
83
|
+
wait_for_settings_update("content.tree.expanded.#{@lv_root.id}", false)
|
84
|
+
visit releaf_content_nodes_path
|
85
|
+
|
86
|
+
expect(page).to have_css('li[data-id="' + @lv_root.id.to_s + '"].collapsed')
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
describe "go_to node" do
|
92
|
+
before do
|
93
|
+
visit edit_releaf_content_node_path @en_root
|
94
|
+
end
|
95
|
+
|
96
|
+
context "when going to node from toolbox list" do
|
97
|
+
it "navigates to targeted node's edit view" do
|
98
|
+
expect(page).to have_no_header(text: 'lv')
|
99
|
+
open_toolbox_dialog "Go to"
|
100
|
+
|
101
|
+
click_link "lv"
|
102
|
+
expect(page).to have_header(text: 'lv')
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
describe "copy node to" do
|
108
|
+
context "when copying node" do
|
109
|
+
it "shows copied node in tree" do
|
110
|
+
find('li[data-id="' + @lv_root.id.to_s + '"] > .collapser-cell button').click
|
111
|
+
find('li[data-id="' + @about_us.id.to_s + '"] > .toolbox-cell button').click
|
112
|
+
click_link("Copy")
|
113
|
+
|
114
|
+
within_dialog do
|
115
|
+
find('label > span', text: "en").click
|
116
|
+
click_button "Copy"
|
117
|
+
end
|
118
|
+
|
119
|
+
expect(page).to have_notification("Copy succeeded")
|
120
|
+
|
121
|
+
find('li[data-id="' + @en_root.id.to_s + '"] > .collapser-cell button').click
|
122
|
+
expect(page).to have_css('li > .node-cell a', text: "about us", count: 2)
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
context "when copying node under itself" do
|
127
|
+
it "displays flash error message" do
|
128
|
+
find('li[data-id="' + @lv_root.id.to_s + '"] > .collapser-cell button').click
|
129
|
+
find('li[data-id="' + @about_us.id.to_s + '"] > .toolbox-cell button').click
|
130
|
+
click_link("Copy")
|
131
|
+
|
132
|
+
within_dialog do
|
133
|
+
find('label > span', text: "about us").click
|
134
|
+
click_button "Copy"
|
135
|
+
end
|
136
|
+
|
137
|
+
error_text = "Node with id #{@about_us.id} has error \"source or descendant node can't be parent of new node\""
|
138
|
+
expect(page).to have_css('.dialog .form-error-box', text: error_text)
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
describe "move node to", js: true do
|
144
|
+
context "when moving node to another parent" do
|
145
|
+
it "moves selected node to new position" do
|
146
|
+
find('li[data-id="' + @lv_root.id.to_s + '"] > .collapser-cell button').click
|
147
|
+
find('li[data-id="' + @about_us.id.to_s + '"] > .toolbox-cell button').click
|
148
|
+
click_link("Move")
|
149
|
+
|
150
|
+
within_dialog do
|
151
|
+
find('label > span', text: "en").click
|
152
|
+
click_button "Move"
|
153
|
+
end
|
154
|
+
|
155
|
+
expect(page).to have_css('.notifications .notification .message', text: "Move succeeded")
|
156
|
+
expect(page).to have_css('li > .node-cell a', text: "about us", count: 0)
|
157
|
+
find('li[data-id="' + @en_root.id.to_s + '"] > .collapser-cell button').click
|
158
|
+
expect(page).to have_css('li > .node-cell a', text: "about us", count: 1)
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
context "when moving node under itself" do
|
163
|
+
it "displays flash error message" do
|
164
|
+
find('li[data-id="' + @lv_root.id.to_s + '"] > .collapser-cell button').click
|
165
|
+
find('li[data-id="' + @about_us.id.to_s + '"] > .toolbox-cell button').click
|
166
|
+
click_link("Move")
|
167
|
+
|
168
|
+
within_dialog do
|
169
|
+
find('label > span', text: "about us").click
|
170
|
+
click_button "Move"
|
171
|
+
end
|
172
|
+
|
173
|
+
error_text = "Node with id #{@about_us.id} has error \"can't be parent to itself\" on attribute \"parent_id\""
|
174
|
+
expect(page).to have_css('.dialog .form-error-box', text: error_text)
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
end
|
179
|
+
|
180
|
+
describe "node order", with_tree: false do
|
181
|
+
def create_child parent, child_text, position=nil
|
182
|
+
|
183
|
+
visit releaf_content_nodes_path
|
184
|
+
open_toolbox_dialog 'Add child', parent, ".view-index .collection li"
|
185
|
+
within_dialog do
|
186
|
+
click_link("Text page")
|
187
|
+
end
|
188
|
+
|
189
|
+
create_resource do
|
190
|
+
fill_in 'Name', with: child_text
|
191
|
+
fill_in "Slug", with: child_text
|
192
|
+
fill_in_richtext 'Text', with: child_text
|
193
|
+
if position
|
194
|
+
select position, from: 'Item position'
|
195
|
+
end
|
196
|
+
end
|
197
|
+
|
198
|
+
end
|
199
|
+
|
200
|
+
it "creates nodes in correct order" do
|
201
|
+
create_child @lv_root, 'a'
|
202
|
+
create_child @lv_root, 'b', 'After a'
|
203
|
+
create_child @lv_root, 'c', 'After b'
|
204
|
+
create_child @lv_root, 'd', 'After b'
|
205
|
+
create_child @lv_root, 'e', 'First'
|
206
|
+
|
207
|
+
visit releaf_content_nodes_path
|
208
|
+
find('li[data-id="' + @lv_root.id.to_s + '"] > .collapser-cell button').click
|
209
|
+
|
210
|
+
within(".collection li[data-level='1'][data-id='#{@lv_root.id}'] ul") do
|
211
|
+
expect( page ).to have_content 'e a b d c'
|
212
|
+
end
|
213
|
+
end
|
214
|
+
|
215
|
+
it "by default adds new nodes as last" do
|
216
|
+
create_child @lv_root, 'a'
|
217
|
+
create_child @lv_root, 'b'
|
218
|
+
create_child @lv_root, 'c'
|
219
|
+
|
220
|
+
visit releaf_content_nodes_path
|
221
|
+
find('li[data-id="' + @lv_root.id.to_s + '"] > .collapser-cell button').click
|
222
|
+
|
223
|
+
within(".collection li[data-level='1'][data-id='#{@lv_root.id}'] ul") do
|
224
|
+
expect( page ).to have_content 'a b c'
|
225
|
+
end
|
226
|
+
end
|
227
|
+
end
|
228
|
+
|
229
|
+
describe "creating node for placeholder model", with_tree: false, with_root: false, js: false do
|
230
|
+
it "create record in association table" do
|
231
|
+
allow_any_instance_of(Releaf::Content::Node::RootValidator).to receive(:validate)
|
232
|
+
visit new_releaf_content_node_path(content_type: 'Bundle')
|
233
|
+
fill_in("resource_name", with: "placeholder model node")
|
234
|
+
expect do
|
235
|
+
click_button 'Save'
|
236
|
+
end.to change { [Node.count, Bundle.count] }.from([0, 0]).to([1, 1])
|
237
|
+
end
|
238
|
+
end
|
239
|
+
end
|