releaf-content 0.2.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (49) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +24 -0
  3. data/app/assets/javascripts/releaf/controllers/releaf/content/nodes.js +88 -0
  4. data/app/assets/stylesheets/releaf/controllers/releaf/content/nodes.scss +234 -0
  5. data/app/builders/releaf/content/builders/action_dialog.rb +60 -0
  6. data/app/builders/releaf/content/builders/dialog.rb +15 -0
  7. data/app/builders/releaf/content/builders/tree.rb +84 -0
  8. data/app/builders/releaf/content/content_type_dialog_builder.rb +74 -0
  9. data/app/builders/releaf/content/copy_dialog_builder.rb +9 -0
  10. data/app/builders/releaf/content/go_to_dialog_builder.rb +9 -0
  11. data/app/builders/releaf/content/move_dialog_builder.rb +9 -0
  12. data/app/builders/releaf/content/nodes/content_form_builder.rb +7 -0
  13. data/app/builders/releaf/content/nodes/form_builder.rb +108 -0
  14. data/app/builders/releaf/content/nodes/index_builder.rb +24 -0
  15. data/app/builders/releaf/content/nodes/toolbox_builder.rb +33 -0
  16. data/app/controllers/releaf/content/nodes_controller.rb +166 -0
  17. data/app/middleware/releaf/content/routes_reloader.rb +25 -0
  18. data/app/validators/releaf/content/node/parent_validator.rb +48 -0
  19. data/app/validators/releaf/content/node/root_validator.rb +43 -0
  20. data/app/validators/releaf/content/node/singleness_validator.rb +102 -0
  21. data/app/views/releaf/content/nodes/content_type_dialog.ruby +1 -0
  22. data/app/views/releaf/content/nodes/copy_dialog.ruby +1 -0
  23. data/app/views/releaf/content/nodes/go_to_dialog.ruby +1 -0
  24. data/app/views/releaf/content/nodes/move_dialog.ruby +1 -0
  25. data/lib/releaf-content.rb +6 -0
  26. data/lib/releaf/content/acts_as_node.rb +73 -0
  27. data/lib/releaf/content/acts_as_node/action_controller/acts/node.rb +17 -0
  28. data/lib/releaf/content/acts_as_node/active_record/acts/node.rb +55 -0
  29. data/lib/releaf/content/builders_autoload.rb +18 -0
  30. data/lib/releaf/content/engine.rb +40 -0
  31. data/lib/releaf/content/node.rb +280 -0
  32. data/lib/releaf/content/node_mapper.rb +9 -0
  33. data/lib/releaf/content/route.rb +93 -0
  34. data/lib/releaf/content/router_proxy.rb +23 -0
  35. data/releaf-content.gemspec +20 -0
  36. data/spec/builders/content/nodes/content_form_builder_spec.rb +24 -0
  37. data/spec/builders/content/nodes/form_builder_spec.rb +218 -0
  38. data/spec/builders/content/nodes/toolbox_builder_spec.rb +108 -0
  39. data/spec/controllers/releaf/content/nodes_controller_spec.rb +21 -0
  40. data/spec/features/nodes_spec.rb +239 -0
  41. data/spec/lib/releaf/content/acts_as_node_spec.rb +118 -0
  42. data/spec/lib/releaf/content/node_spec.rb +779 -0
  43. data/spec/lib/releaf/content/route_spec.rb +85 -0
  44. data/spec/middleware/routes_reloader_spec.rb +48 -0
  45. data/spec/routing/node_mapper_spec.rb +142 -0
  46. data/spec/validators/content/node/parent_validator_spec.rb +56 -0
  47. data/spec/validators/content/node/root_validator_spec.rb +69 -0
  48. data/spec/validators/content/node/singleness_validator_spec.rb +145 -0
  49. metadata +145 -0
@@ -0,0 +1,118 @@
1
+ require "rails_helper"
2
+
3
+ class ContactFormController < ActionController::Base
4
+ acts_as_node
5
+ end
6
+
7
+ describe ActsAsNode do
8
+ before do
9
+ Book.acts_as_node
10
+ end
11
+
12
+ describe ".classes" do
13
+ it "returns all registerd classes" do
14
+ expect(ActsAsNode.classes).to include("ContactFormController", "Book")
15
+ end
16
+ end
17
+
18
+ describe ".acts_as_node" do
19
+ it "have configuration options for params and fields available through acts_as_node_configuration class method" do
20
+ expect(Book.acts_as_node_configuration).to eq(params: nil, fields: nil)
21
+
22
+ Book.acts_as_node params: ["x"], fields: ["a"]
23
+ expect(Book.acts_as_node_configuration).to eq(params: ["x"], fields: ["a"])
24
+ end
25
+
26
+ it "has hard typed configuration options" do
27
+ expect{ Book.acts_as_node xxxx: ["x"] }.to raise_error(ArgumentError, "unknown keyword: xxxx")
28
+ end
29
+ end
30
+
31
+ describe ActiveRecord::Acts::Node do
32
+ context "when model acts as node" do
33
+ it "has name included within ActsAsNode.classes" do
34
+ expect(ActsAsNode.classes.include?(Book.to_s)).to be true
35
+ end
36
+ end
37
+
38
+ describe "#node" do
39
+ it "returns corresponding node object" do
40
+ allow_any_instance_of(Releaf::Content::Node::RootValidator).to receive(:validate)
41
+ node = create(:node, content_type: "Book", content_attributes: {title: "xx"})
42
+ expect(Book.last.node).to eq(node)
43
+ end
44
+ end
45
+
46
+ context ".acts_as_node_params" do
47
+ before do
48
+ allow_any_instance_of(Releaf::Core::ResourceParams).to receive(:values).and_return(["a", "b"])
49
+ end
50
+
51
+ context "when `params` configuration is nil" do
52
+ it "returns model params with `id` param" do
53
+ allow(Book).to receive(:acts_as_node_configuration).and_return(params: nil)
54
+ expect(Releaf::Core::ResourceParams).to receive(:new).with(Book).and_call_original
55
+ expect(Book.acts_as_node_params).to eq(["a", "b", :id])
56
+ end
57
+ end
58
+
59
+ context "when `params` configuration is not nil" do
60
+ it "returns configuration values with `id` param" do
61
+ allow(Book).to receive(:acts_as_node_configuration).and_return(params: ["c", "d"])
62
+ expect(Book.acts_as_node_params).to eq(["c", "d", :id])
63
+ end
64
+ end
65
+ end
66
+
67
+ context ".acts_as_node_fields" do
68
+ before do
69
+ allow_any_instance_of(Releaf::Core::ResourceFields).to receive(:values).and_return(["a", "b"])
70
+ end
71
+
72
+ context "when `fields` configuration is nil" do
73
+ it "returns model fields" do
74
+ allow(Book).to receive(:acts_as_node_configuration).and_return(fields: nil)
75
+ expect(Releaf::Core::ResourceFields).to receive(:new).with(Book).and_call_original
76
+ expect(Book.acts_as_node_fields).to eq(["a", "b"])
77
+ end
78
+ end
79
+
80
+ context "when `fields` configuration is not nil" do
81
+ it "returns configuration values" do
82
+ allow(Book).to receive(:acts_as_node_configuration).and_return(fields: ["c", "d"])
83
+ expect(Book.acts_as_node_fields).to eq(["c", "d"])
84
+ end
85
+ end
86
+ end
87
+
88
+ context ".nodes" do
89
+ it "loads tree nodes" do
90
+ expect(Node).to receive(:where).with(content_type: Book.name)
91
+ Book.nodes
92
+ end
93
+
94
+ it "returns relation" do
95
+ expect(Book.nodes.class).to eq(Node::ActiveRecord_Relation)
96
+ end
97
+ end
98
+ end
99
+
100
+ describe ActionController::Acts::Node do
101
+ context "when controller acts as node" do
102
+ it "has name included within ActsAsNode.classes" do
103
+ expect(ActsAsNode.classes.include?(ContactFormController.to_s)).to be true
104
+ end
105
+ end
106
+
107
+ context ".nodes" do
108
+ it "loads tree nodes" do
109
+ expect(Node).to receive(:where).with(content_type: ContactFormController.name)
110
+ ContactFormController.nodes
111
+ end
112
+
113
+ it "returns array" do
114
+ expect(ContactFormController.nodes.class).to eq(Node::ActiveRecord_Relation)
115
+ end
116
+ end
117
+ end
118
+ end
@@ -0,0 +1,779 @@
1
+ require "rails_helper"
2
+
3
+ describe Node do
4
+ class PlainNode < ActiveRecord::Base
5
+ include Releaf::Content::Node
6
+ self.table_name = "nodes"
7
+ end
8
+
9
+ let(:plain_subject){ PlainNode.new }
10
+
11
+ it { is_expected.to accept_nested_attributes_for(:content) }
12
+ it { is_expected.to belong_to(:content) }
13
+
14
+ it "includes Releaf::Content::Node module" do
15
+ expect( Node.included_modules ).to include Releaf::Content::Node
16
+ end
17
+
18
+ describe "validations" do
19
+ it { is_expected.to validate_presence_of(:name) }
20
+ it { is_expected.to validate_presence_of(:slug) }
21
+ it { is_expected.to validate_presence_of(:content_type) }
22
+ it { is_expected.to validate_uniqueness_of(:slug).scoped_to(:parent_id) }
23
+ it { is_expected.to validate_length_of(:name).is_at_most(255) }
24
+ it { is_expected.to validate_length_of(:slug).is_at_most(255) }
25
+ end
26
+
27
+ describe "after save" do
28
+ it "sets node update to current time" do
29
+ expect( Node ).to receive(:updated).once
30
+ create(:node)
31
+ end
32
+ end
33
+
34
+ describe ".active (scope)" do
35
+ it "returns active nodes" do
36
+ expect( Node ).to receive(:where).with(active: true).and_return('foo')
37
+ expect( Node.active ).to eq 'foo'
38
+ end
39
+ end
40
+
41
+ describe "#content_class" do
42
+ context 'when #content_type is nil' do
43
+ it 'returns nil' do
44
+ subject.content_type = nil
45
+ expect( subject.content_class ).to be_nil
46
+ end
47
+ end
48
+
49
+ context "when #content_type is blank string" do
50
+ it 'returns nil' do
51
+ subject.content_type = ""
52
+ expect( subject.content_class ).to be_nil
53
+ end
54
+ end
55
+
56
+ context "when #content_type is not blank" do
57
+ it "constantizes it" do
58
+ subject.content_type = "Node"
59
+ expect( subject.content_class ).to eq Node
60
+ end
61
+ end
62
+ end
63
+
64
+ describe "#to_s" do
65
+ it "returns name" do
66
+ expect(subject.to_s).to eq(subject.name)
67
+ end
68
+ end
69
+
70
+ describe "#locale" do
71
+ before do
72
+ root = create(:node, locale: "lv")
73
+ parent = create(:text_page_node, locale: nil, parent_id: root.id)
74
+ @child1 = create(:text_page_node, locale: nil, parent_id: parent.id)
75
+ @child2 = create(:text_page_node, parent_id: parent.id, locale: "en")
76
+ end
77
+
78
+ context "when node locale is nil" do
79
+ it "uses closest parent locale" do
80
+ expect(@child1.locale).to eq("lv")
81
+ end
82
+ end
83
+
84
+ context "when object node have locale" do
85
+ it "uses closest parent locale" do
86
+ expect(@child2.locale).to eq("en")
87
+ end
88
+ end
89
+ end
90
+
91
+ describe "#destroy" do
92
+ context "when content object class exists" do
93
+ let!(:node) { create(:home_page_node) }
94
+
95
+ it "deletes record" do
96
+ expect { node.destroy }.to change { Node.count }.by(-1)
97
+ end
98
+
99
+ it "deletes associated record" do
100
+ expect { node.destroy }.to change { HomePage.count }.by(-1)
101
+ end
102
+ end
103
+
104
+ context "when content object class doesn't exists" do
105
+ let!(:node) { create(:home_page_node) }
106
+ before do
107
+ node.update_columns(content_type: 'NonExistingTestModel')
108
+ end
109
+
110
+ it "deletes record" do
111
+ expect { node.destroy }.to change { Node.count }.by(-1)
112
+ end
113
+ end
114
+
115
+ it "sets node update to current time" do
116
+ node = create(:node)
117
+ expect( Node ).to receive(:updated).once
118
+ node.destroy
119
+ end
120
+ end
121
+
122
+ describe "#attributes_to_not_copy" do
123
+ it "returns array with attributes" do
124
+ expect( subject.attributes_to_not_copy ).to match_array %w[content_id depth id item_position lft rgt slug created_at updated_at]
125
+ end
126
+ end
127
+
128
+ describe "#attributes_to_copy" do
129
+ it "returns object attributes excluding #attributes_to_not_copy" do
130
+ node = Node.new
131
+ allow( node ).to receive(:attributes_to_not_copy).and_return(%w[lft rgt])
132
+ expect( node.attributes_to_copy ).to eq(Node.column_names - %w[lft rgt])
133
+ end
134
+ end
135
+
136
+ describe "#reasign_slug" do
137
+ it "updates slug" do
138
+ node = create(:node)
139
+ old_slug = node.slug
140
+ node.name = 'woo hoo'
141
+ expect { node.reasign_slug }.to change { node.slug }.from(old_slug).to('woo-hoo')
142
+ end
143
+ end
144
+
145
+ describe "#save_under" do
146
+ let(:node1) { create(:node) }
147
+
148
+ it "saves node nuder node with given node_id" do
149
+ node2 = create(:text_page_node, parent: node1)
150
+
151
+ new_node = FactoryGirl.build(:text_page_node)
152
+ new_node.send(:save_under, node2.id)
153
+ expect( new_node ).to_not be_new_record
154
+ expect( new_node.parent ).to eq node2
155
+ end
156
+
157
+ it "maintains node name, updates slug and then saves record" do
158
+ new_node = FactoryGirl.build(:text_page_node)
159
+ expect( new_node ).to receive(:maintain_name).ordered.and_call_original
160
+ expect( new_node ).to receive(:reasign_slug).ordered.and_call_original
161
+ expect( new_node ).to receive(:save!).ordered.and_call_original
162
+ new_node.save_under(node1.id)
163
+ end
164
+
165
+ context "when #validate_root_locale_uniqueness? returns true" do
166
+ it "sets locale to nil" do
167
+ new_node = FactoryGirl.build(:node)
168
+ allow(new_node).to receive(:validate_root_locale_uniqueness?).and_return(true)
169
+ new_node.locale = 'xx'
170
+ expect { new_node.save_under(nil) }.to change { new_node.locale }.from('xx').to(nil)
171
+ end
172
+ end
173
+
174
+ context "when #validate_root_locale_uniqueness? returns false" do
175
+ it "doesn't set locale to nil" do
176
+ new_node = FactoryGirl.build(:node)
177
+ allow(new_node).to receive(:validate_root_locale_uniqueness?).and_return(false)
178
+ new_node.locale = 'xx'
179
+ expect { new_node.save_under(nil) }.to_not change { new_node.locale }.from('xx')
180
+ end
181
+ end
182
+
183
+ end
184
+
185
+
186
+ describe "#duplicate_content" do
187
+
188
+ context "when #content_id is blank" do
189
+ let(:node) { create(:node) }
190
+
191
+ it "returns nil" do
192
+ expect( node.duplicate_content ).to be_nil
193
+ end
194
+ end
195
+
196
+ context "when #content_id is not blank" do
197
+ let(:node) { create(:home_page_node) }
198
+
199
+ it "returns saved duplicated content" do
200
+ content = double('new content')
201
+ expect( content ).to receive(:save!)
202
+ expect( node ).to receive_message_chain(:content, :dup).and_return(content)
203
+ expect( node.duplicate_content ).to eq content
204
+ end
205
+
206
+ it "doesn't return same as original content" do
207
+ result = node.duplicate_content
208
+ expect( result ).to be_an_instance_of HomePage
209
+ expect( result.id ).to_not eq node.content_id
210
+ end
211
+ end
212
+ end
213
+
214
+ describe "#copy_attributes_from" do
215
+ let(:source_node) { create(:node, active: false) }
216
+
217
+ it "copies #attributes_to_copy attributes" do
218
+
219
+ allow( source_node ).to receive(:attributes_to_copy).and_return(['name'])
220
+
221
+ expect( source_node ).to receive(:name).and_call_original
222
+ expect( source_node ).to_not receive(:parent_id)
223
+ expect( source_node ).to_not receive(:content_type)
224
+ expect( source_node ).to_not receive(:active)
225
+
226
+ new_node = Node.new
227
+
228
+ new_node.copy_attributes_from source_node
229
+
230
+ expect( new_node.parent_id ).to be_nil
231
+ expect( new_node.content_type ).to be_nil
232
+ expect( new_node.active ).to be_truthy
233
+ end
234
+ end
235
+
236
+ describe "#duplicate_under!" do
237
+ let!(:source_node) { create(:node, locale: "lv") }
238
+ let!(:target_node) { create(:node, locale: "en") }
239
+
240
+ before do
241
+ allow_any_instance_of(Releaf::Content::Node::RootValidator).to receive(:validate)
242
+ end
243
+
244
+ it "creates duplicated node under target node" do
245
+ new_node = Node.new
246
+ duplicated_content = double('content', id: 1234)
247
+ expect( Node ).to receive(:new).ordered.and_return(new_node)
248
+ expect( new_node ).to receive(:copy_attributes_from).with(source_node).ordered.and_call_original
249
+ expect( source_node ).to receive(:duplicate_content).ordered.and_return(duplicated_content)
250
+ expect( new_node ).to receive(:content_id=).with(1234).ordered
251
+ expect( new_node ).to receive(:save_under).with(target_node.id).ordered.and_call_original
252
+ source_node.duplicate_under!(target_node.id)
253
+ end
254
+
255
+ it "doesn't update settings timestamp" do
256
+ expect( Node ).to_not receive(:updated)
257
+ source_node.duplicate_under!(target_node.id)
258
+ end
259
+ end
260
+
261
+ describe "#copy", create_nodes: true do
262
+ before create_nodes: true do
263
+ @home_page_node = create(:home_page_node, locale: "lv")
264
+ @home_page_node_2 = create(:home_page_node, locale: "en")
265
+ @text_page_node_3 = create(:text_page_node, parent_id: @home_page_node_2.id)
266
+ @text_page_node_4 = create(:text_page_node, parent_id: @text_page_node_3.id)
267
+ @text_page_node_5 = create(:text_page_node, parent_id: @text_page_node_4.id)
268
+
269
+ # it is important to reload nodes, otherwise associations will return empty set
270
+ @home_page_node.reload
271
+ @home_page_node_2.reload
272
+ @text_page_node_3.reload
273
+ @text_page_node_4.reload
274
+ end
275
+
276
+ context "when one of children becomes invalid" do
277
+ before do
278
+ @text_page_node_4.name = nil
279
+ @text_page_node_4.save(validate: false)
280
+ end
281
+
282
+ it "raises ActiveRecord::RecordInvalid" do
283
+ expect { @text_page_node_3.copy(@home_page_node.id) }.to raise_error ActiveRecord::RecordInvalid
284
+ end
285
+
286
+ it "raises error on node being copied" do
287
+ begin
288
+ @text_page_node_3.copy(@home_page_node.id)
289
+ rescue ActiveRecord::RecordInvalid => e
290
+ expect( e.record ).to eq @text_page_node_3
291
+ end
292
+ expect(@text_page_node_3.errors.messages).to eq(base: ["descendant invalid"])
293
+ end
294
+
295
+ it "doesn't create any new nodes" do
296
+ expect do
297
+ begin
298
+ @text_page_node_3.copy(@home_page_node.id)
299
+ rescue ActiveRecord::RecordInvalid
300
+ end
301
+ end.to_not change { Node.count }
302
+ end
303
+
304
+ it "doesn't update settings timestamp" do
305
+ expect( Node ).to_not receive(:updated)
306
+ begin
307
+ @text_page_node_3.copy(@home_page_node.id)
308
+ rescue ActiveRecord::RecordInvalid
309
+ end
310
+ end
311
+
312
+ end
313
+
314
+
315
+ context "with corect parent_id" do
316
+ it "creates node along with descendant nodes" do
317
+ expect{ @text_page_node_3.copy(@home_page_node.id) }.to change{ Node.count }.by( @text_page_node_3.descendants.size + 1 )
318
+ end
319
+
320
+ it "correctly copies attributes" do
321
+ allow( @text_page_node_3 ).to receive(:children).and_return([@text_page_node_4])
322
+ allow( @text_page_node_4 ).to receive(:children).and_return([@text_page_node_5])
323
+
324
+ @text_page_node_3.update_attribute(:active, false)
325
+ @text_page_node_4.update_attribute(:active, false)
326
+
327
+ allow( @text_page_node_3 ).to receive(:attributes_to_copy).and_return(["name", "parent_id", "content_type"])
328
+
329
+ expect( @text_page_node_3 ).to receive(:duplicate_under!).and_call_original.ordered
330
+ expect( @text_page_node_4 ).to receive(:duplicate_under!).and_call_original.ordered
331
+ expect( @text_page_node_5 ).to receive(:duplicate_under!).and_call_original.ordered
332
+
333
+ @text_page_node_3.copy(@home_page_node.id)
334
+
335
+ @node_2_copy = @home_page_node.children.first
336
+ @node_3_copy = @node_2_copy.children.first
337
+ @node_4_copy = @node_3_copy.children.first
338
+
339
+ # new nodes by default are active, however we stubbed
340
+ # #attributes_to_copy of @test_node_2 to not return active attribute
341
+ # Also we updated @test_node_2#active to be false.
342
+ # However copy is active, because active attribute wasn't copied
343
+ expect( @node_2_copy ).to be_active
344
+ # for copy of @text_page_node_3 active attribute was copied however, as it
345
+ # should have been
346
+ expect( @node_3_copy ).to_not be_active
347
+ expect( @node_4_copy ).to be_active
348
+
349
+ expect( @node_2_copy.name ).to eq @text_page_node_3.name
350
+ expect( @node_3_copy.name ).to eq @text_page_node_4.name
351
+ expect( @node_4_copy.name ).to eq @text_page_node_5.name
352
+ end
353
+
354
+ it "updates settings timestamp only once" do
355
+ expect( Node ).to receive(:updated).once.and_call_original
356
+ @text_page_node_3.copy(@home_page_node.id)
357
+ end
358
+
359
+ context "when parent_id is nil" do
360
+ it "creates new node" do
361
+ allow_any_instance_of(Releaf::Content::Node::RootValidator).to receive(:validate)
362
+ expect{ @text_page_node_3.copy(nil) }.to change{ Node.count }.by(3)
363
+ end
364
+ end
365
+
366
+ context "when copying root nodes", create_nodes: false do
367
+ context "when root locale uniqueness is validated" do
368
+ it "resets locale to nil" do
369
+ @text_page_node = create(:home_page_node, locale: 'en')
370
+ allow_any_instance_of(Node).to receive(:validate_root_locale_uniqueness?).and_return(true)
371
+ @text_page_node.copy(nil)
372
+ expect( Node.last.locale ).to eq nil
373
+ end
374
+ end
375
+
376
+ context "when root locale uniqueness is not validated" do
377
+ it "doesn't reset locale to nil" do
378
+ @text_page_node = create(:home_page_node, locale: 'en')
379
+ allow_any_instance_of(Node).to receive(:validate_root_locale_uniqueness?).and_return(false)
380
+ @text_page_node.copy(nil)
381
+ expect( Node.last.locale ).to eq 'en'
382
+ end
383
+ end
384
+ end
385
+ end
386
+
387
+ context "with nonexistent parent_id" do
388
+ it "raises ActiveRecord::RecordInvalid" do
389
+ expect { @text_page_node_3.copy(99991) }.to raise_error(ActiveRecord::RecordInvalid)
390
+ end
391
+ end
392
+
393
+ context "with same parent_id as node.id" do
394
+ it "raises ActiveRecord::RecordInvalid" do
395
+ expect{ @text_page_node_3.copy(@text_page_node_3.id) }.to raise_error(ActiveRecord::RecordInvalid)
396
+ end
397
+ end
398
+
399
+ context "when copying to child node" do
400
+ it "raises ActiveRecord::RecordInvalid" do
401
+ expect{ @text_page_node_3.copy(@text_page_node_4.id) }.to raise_error(ActiveRecord::RecordInvalid)
402
+ end
403
+ end
404
+ end
405
+
406
+ describe ".children_max_item_position" do
407
+ before do
408
+ @home_page_node = create(:home_page_node, item_position: 1, locale: "lv")
409
+ @home_page_node_2 = create(:home_page_node, item_position: 2, locale: "en")
410
+ @text_page_node_3 = create(:text_page_node, parent_id: @home_page_node_2.id, item_position: 1)
411
+ @text_page_node_4 = create(:text_page_node, parent_id: @home_page_node_2.id, item_position: 2)
412
+
413
+ # it is important to reload nodes, otherwise associations will return empty set
414
+ @home_page_node.reload
415
+ @home_page_node_2.reload
416
+ @text_page_node_3.reload
417
+ @text_page_node_4.reload
418
+ end
419
+
420
+ context "when passing nil" do
421
+ it "returns max item_position of root nodes" do
422
+ expect( Node.children_max_item_position(nil) ).to eq 2
423
+ end
424
+ end
425
+
426
+ context "when passing node with children" do
427
+ it "returns max item_position of node children" do
428
+ expect( Node.children_max_item_position(@home_page_node_2) ).to eq 2
429
+ end
430
+ end
431
+
432
+ context "when passing node without children" do
433
+ it "returns 0" do
434
+ expect( Node.children_max_item_position(@text_page_node_4) ).to eq 0
435
+ end
436
+ end
437
+ end
438
+
439
+ describe "#move" do
440
+ before do
441
+ @home_page_node = create(:home_page_node, locale: "lv")
442
+ @home_page_node_2 = create(:home_page_node, locale: "en")
443
+ @text_page_node_3 = create(:text_page_node, parent_id: @home_page_node_2.id)
444
+ @text_page_node_4 = create(:text_page_node, parent_id: @text_page_node_3.id)
445
+
446
+ # it is important to reload nodes, otherwise associations will return empty set
447
+ @home_page_node.reload
448
+ @home_page_node_2.reload
449
+ @text_page_node_3.reload
450
+ @text_page_node_4.reload
451
+ end
452
+
453
+ context "when one of children becomes invalid" do
454
+ before do
455
+ @text_page_node_4.name = nil
456
+ @text_page_node_4.save(validate: false)
457
+ end
458
+
459
+ it "raises ActiveRecord::RecordInvalid" do
460
+ expect { @text_page_node_3.move(@home_page_node.id) }.to raise_error ActiveRecord::RecordInvalid
461
+ end
462
+
463
+ it "raises error on node being moved, even tought descendant has error" do
464
+ begin
465
+ @text_page_node_3.move(@home_page_node.id)
466
+ rescue ActiveRecord::RecordInvalid => e
467
+ expect( e.record ).to eq @text_page_node_3
468
+ end
469
+
470
+ expect(@text_page_node_3.errors.messages).to eq(name: [], base: ["descendant invalid"])
471
+ end
472
+ end
473
+
474
+ context "when moving existing node to other nodes child's position" do
475
+ it "changes parent_id" do
476
+ expect{ @text_page_node_3.move(@home_page_node.id) }.to change{ Node.find(@text_page_node_3.id).parent_id }.from(@home_page_node_2.id).to(@home_page_node.id)
477
+ end
478
+ end
479
+
480
+ context "when moving to self child's position" do
481
+ it "raises ActiveRecord::RecordInvalid" do
482
+ expect{ @text_page_node_3.move(@text_page_node_3.id) }.to raise_error(ActiveRecord::RecordInvalid)
483
+ end
484
+ end
485
+
486
+ context "when passing nil as target node" do
487
+ it "updates parent_id" do
488
+ allow_any_instance_of(Releaf::Content::Node::RootValidator).to receive(:validate)
489
+ @home_page_node.destroy
490
+ expect{ @text_page_node_3.move(nil) }.to change { Node.find(@text_page_node_3.id).parent_id }.to(nil)
491
+ end
492
+ end
493
+
494
+ context "when passing nonexistent target node's id" do
495
+ it "raises ActiveRecord::RecordInvalid" do
496
+ expect{ @text_page_node_3.move(998123) }.to raise_error(ActiveRecord::RecordInvalid)
497
+ end
498
+ end
499
+
500
+ end
501
+
502
+ describe "#maintain_name" do
503
+ let(:root) { create(:home_page_node) }
504
+ let(:node) { create(:text_page_node, parent_id: root.id, name: "Test node") }
505
+ let(:sibling) { create(:text_page_node, parent_id: root.id, name: "Test node(1)") }
506
+
507
+ context "when node don't have sibling/s with same name" do
508
+ it "does not changes node's name" do
509
+ new_node = Node.new(name: "another name", parent_id: root.id)
510
+ expect{ new_node.maintain_name }.to_not change{new_node.name}
511
+ end
512
+ end
513
+
514
+ context "when node have sibling/s with same name" do
515
+ it "changes node's name" do
516
+ new_node = Node.new(name: node.name, parent_id: root.id)
517
+ expect{ new_node.maintain_name }.to change{new_node.name}.from(node.name).to("#{node.name}(1)")
518
+ end
519
+
520
+ it "increments node's name number" do
521
+ sibling
522
+ new_node = Node.new(name: node.name, parent_id: root.id)
523
+ expect{ new_node.maintain_name }.to change{new_node.name}.from(node.name).to("#{node.name}(2)")
524
+ end
525
+ end
526
+ end
527
+
528
+ describe ".updated_at" do
529
+ it "returns last node update time" do
530
+ expect( Releaf::Settings ).to receive(:[]).with('releaf.content.nodes.updated_at').and_return('test')
531
+ expect( Node.updated_at ).to eq 'test'
532
+ end
533
+ end
534
+
535
+ describe ".updated" do
536
+ it "returns last node update time" do
537
+ allow(Time).to receive(:now).and_return("asd")
538
+ expect( Releaf::Settings ).to receive(:[]=).with('releaf.content.nodes.updated_at', "asd")
539
+ Node.updated
540
+ end
541
+ end
542
+
543
+ describe "#available?" do
544
+ let(:root) { create(:home_page_node, active: true) }
545
+ let(:node_ancestor) { create(:text_page_node, parent_id: root.id, active: true) }
546
+ let(:node) { create(:text_page_node, parent_id: node_ancestor.id, active: true) }
547
+
548
+ context "when object and all its ancestors are active" do
549
+ it "returns true" do
550
+ expect( node ).to be_available
551
+ end
552
+ end
553
+
554
+ context "when object itself is not active" do
555
+ it "returns false" do
556
+ node.update_attribute(:active, false)
557
+ expect( node ).to_not be_available
558
+ end
559
+ end
560
+
561
+ context "when any of object ancestors are not active" do
562
+ it "returns false" do
563
+ node_ancestor.update_attribute(:active, false)
564
+ expect( node ).to_not be_available
565
+ end
566
+ end
567
+ end
568
+
569
+ describe ".valid_node_content_classes" do
570
+ it "returns array of constantized .valid_node_content_class_names" do
571
+ expect( Node ).to receive(:valid_node_content_class_names).with(42).and_return(['TextPage', 'HomePagesController'])
572
+ expect( Node.valid_node_content_classes(42) ).to eq [TextPage, HomePagesController]
573
+ end
574
+ end
575
+
576
+ describe ".valid_node_content_class_names" do
577
+ it "returns class names for Node#content_type that can be used to create valid node" do
578
+ expect( ActsAsNode ).to receive(:classes).and_return(%w[BadNode GoodNode])
579
+
580
+ node_1 = double('BadNode')
581
+ allow(node_1).to receive(:valid?)
582
+ node_1_errors = double("Node 1 Errors object")
583
+ expect( node_1_errors ).to receive(:[]).with(:content_type).and_return(['some error'])
584
+ allow(node_1).to receive(:errors).and_return(node_1_errors)
585
+
586
+ node_2 = double('GoodNode')
587
+ allow(node_2).to receive(:valid?)
588
+ node_2_errors = double("Node 2 Errors object")
589
+ expect( node_2_errors ).to receive(:[]).with(:content_type).and_return(nil)
590
+ allow(node_2).to receive(:errors).and_return(node_2_errors)
591
+
592
+ expect( Node ).to receive(:new).with(hash_including(parent_id: 52, content_type: 'BadNode')).and_return(node_1)
593
+ expect( Node ).to receive(:new).with(hash_including(parent_id: 52, content_type: 'GoodNode')).and_return(node_2)
594
+
595
+ expect( Node.valid_node_content_class_names(52) ).to eq %w[GoodNode]
596
+ end
597
+
598
+ end
599
+
600
+ describe "#locale_selection_enabled?" do
601
+ it "returns false" do
602
+ expect( plain_subject.locale_selection_enabled? ).to be false
603
+ end
604
+ end
605
+
606
+ describe "#build_content" do
607
+ it "builds new content and assigns to #content" do
608
+ subject.content_type = 'TextPage'
609
+ params = {text_html: 'test'}
610
+ expect( TextPage ).to receive(:new).with(params).and_call_original
611
+ subject.build_content(params)
612
+ expect( subject.content ).to be_an_instance_of TextPage
613
+ expect( subject.content.text_html ).to eq 'test'
614
+ end
615
+ end
616
+
617
+ describe "#validate_root_locale_uniqueness?" do
618
+ before do
619
+ allow( subject ).to receive(:root?).and_return(true)
620
+ allow( subject ).to receive(:locale_selection_enabled?).and_return(true)
621
+ end
622
+
623
+ context "when #locale_selection_enabled? and #root? both are true" do
624
+ it "returns true" do
625
+ expect( subject.send(:validate_root_locale_uniqueness?) ).to be_truthy
626
+ end
627
+ end
628
+
629
+ context "when #locale_selection_enabled? is false" do
630
+ it "returns false" do
631
+ allow( subject ).to receive(:locale_selection_enabled?).and_return(false)
632
+ expect( subject.send(:validate_root_locale_uniqueness?) ).to be_falsy
633
+ end
634
+ end
635
+
636
+ context "when #root? is false" do
637
+ it "returns false" do
638
+ allow( subject ).to receive(:root?).and_return(false)
639
+ expect( subject.send(:validate_root_locale_uniqueness?) ).to be_falsy
640
+ end
641
+ end
642
+ end
643
+
644
+ describe "#validate_parent_node_is_not_self" do
645
+ let(:node1) { create(:node, locale: "lv") }
646
+
647
+ it "is called during validations" do
648
+ expect( subject ).to receive(:validate_parent_node_is_not_self)
649
+ subject.valid?
650
+ end
651
+
652
+ context "when #parent_id matches #id" do
653
+ it "adds error on #parent_id" do
654
+ node1.parent_id = node1.id
655
+ node1.send(:validate_parent_node_is_not_self)
656
+ expect( node1.errors[:parent_id] ).to include "can't be parent to itself"
657
+ end
658
+ end
659
+
660
+ context "when parent is nil" do
661
+ it "does nothing" do
662
+ node1.parent_id = nil
663
+ node1.send(:validate_parent_node_is_not_self)
664
+ expect( node1.errors[:parent_id] ).to be_blank
665
+ end
666
+ end
667
+
668
+ context "#id matches #parent_id" do
669
+ it "does nothing" do
670
+ node2 = create(:node, locale: "en")
671
+ node1.parent_id = node2.id
672
+ node1.send(:validate_parent_is_not_descendant)
673
+ expect( node1.errors[:parent_id] ).to be_blank
674
+ end
675
+ end
676
+
677
+
678
+ end
679
+
680
+ describe "#validate_parent_is_not_descendant" do
681
+ before with_tree: true do
682
+ @node1 = create(:home_page_node, locale: "en")
683
+ @node2 = create(:text_page_node, parent: @node1)
684
+ @node3 = create(:text_page_node, parent: @node2)
685
+ @node4 = create(:home_page_node, locale: "lv")
686
+
687
+ @node1.reload
688
+ @node2.reload
689
+ @node3.reload
690
+ end
691
+
692
+ it "is called during validations" do
693
+ expect( subject ).to receive(:validate_parent_is_not_descendant)
694
+ subject.valid?
695
+ end
696
+
697
+ context "when #parent_id matches #id of one of descadents", with_tree: true do
698
+ it "adds error on #parent_id" do
699
+ @node1.parent_id = @node3.id
700
+ @node1.send(:validate_parent_is_not_descendant)
701
+ expect( @node1.errors[:parent_id] ).to include "descendant can't be parent"
702
+ end
703
+ end
704
+
705
+ context "when parent is nil", with_tree: true do
706
+ it "does nothing" do
707
+ @node1.parent_id = nil
708
+ @node1.send(:validate_parent_is_not_descendant)
709
+ expect( @node1.errors[:parent_id] ).to be_blank
710
+ end
711
+ end
712
+
713
+ context "when there are no descadents with #id is #self#parent_id", with_tree: true do
714
+ it "does nothing" do
715
+ @node1.parent_id = @node4.id
716
+ @node1.send(:validate_parent_is_not_descendant)
717
+ expect( @node1.errors[:parent_id] ).to be_blank
718
+ end
719
+ end
720
+ end
721
+
722
+ describe "#add_error_and_raise" do
723
+ it "raises error" do
724
+ expect { subject.add_error_and_raise('test error') }.to raise_error(ActiveRecord::RecordInvalid)
725
+ end
726
+
727
+ it "adds record on base and raise ActiveRecord::RecordInvalid" do
728
+ begin
729
+ subject.add_error_and_raise('test error')
730
+ rescue ActiveRecord::RecordInvalid => e
731
+ expect( e.record.errors[:base] ).to include 'test error'
732
+ end
733
+ end
734
+ end
735
+
736
+ describe "#prevent_auto_update_settings_timestamp" do
737
+ it "sets #prevent_auto_update_settings_timestamp? to true within block" do
738
+ expect do
739
+ subject.prevent_auto_update_settings_timestamp do
740
+ expect( subject.send(:prevent_auto_update_settings_timestamp?) ).to be_truthy
741
+ end
742
+ end.to_not change { subject.send(:prevent_auto_update_settings_timestamp?) }.from(false)
743
+ end
744
+ end
745
+
746
+ describe "#update_settings_timestamp" do
747
+ it "calls .updated" do
748
+ expect( Node ).to receive(:updated).and_call_original
749
+ subject.send(:update_settings_timestamp)
750
+ end
751
+
752
+ context "when #prevent_auto_update_settings_timestamp? is false" do
753
+ it "is called after save" do
754
+ node = FactoryGirl.build(:node)
755
+ allow( node ).to receive(:prevent_auto_update_settings_timestamp?).and_return(false)
756
+ expect( node ).to receive(:update_settings_timestamp).and_call_original
757
+ node.save!
758
+ end
759
+ end
760
+
761
+ context "when #prevent_auto_update_settings_timestamp? is true" do
762
+ it "is not called after save" do
763
+ node = FactoryGirl.build(:node)
764
+ allow( node ).to receive(:prevent_auto_update_settings_timestamp?).and_return(true)
765
+ expect( node ).to_not receive(:update_settings_timestamp)
766
+ node.save!
767
+ end
768
+ end
769
+ end
770
+
771
+ describe "#path" do
772
+ it "returns relative path of node" do
773
+ node = described_class.new(slug: 'foo')
774
+ node2 = described_class.new(slug: 'bar', parent: node)
775
+ expect( node.path ).to eq '/foo'
776
+ expect( node2.path ).to eq '/foo/bar'
777
+ end
778
+ end
779
+ end