Completed
Push — master ( c7767b...f548dd )
by Daniel
11:31
created

testMarkChildrenDoesntUnmarkPreviouslyMarked()   B

Complexity

Conditions 1
Paths 1

Size

Total Lines 43
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 1 Features 0
Metric Value
cc 1
eloc 13
c 1
b 1
f 0
nc 1
nop 0
dl 0
loc 43
rs 8.8571
1
<?php
2
3
use SilverStripe\ORM\ValidationException;
4
use SilverStripe\ORM\Versioning\Versioned;
5
use SilverStripe\ORM\DataObject;
6
7
class HierarchyTest extends SapphireTest {
8
9
	protected static $fixture_file = 'HierarchyTest.yml';
10
11
	protected $requiredExtensions = array(
12
		'HierarchyTest_Object' => array('SilverStripe\\ORM\\Hierarchy\\Hierarchy', 'SilverStripe\\ORM\\Versioning\\Versioned'),
13
		'HierarchyHideTest_Object' => array('SilverStripe\\ORM\\Hierarchy\\Hierarchy', 'SilverStripe\\ORM\\Versioning\\Versioned'),
14
	);
15
16
	protected $extraDataObjects = array(
17
		'HierarchyTest_Object',
18
		'HierarchyHideTest_Object'
19
	);
20
21
	/**
22
	 * Test the Hierarchy prevents infinite loops.
23
	 */
24
	public function testPreventLoop() {
25
		$obj2 = $this->objFromFixture('HierarchyTest_Object', 'obj2');
26
		$obj2aa = $this->objFromFixture('HierarchyTest_Object', 'obj2aa');
27
28
		$obj2->ParentID = $obj2aa->ID;
0 ignored issues
show
Documentation introduced by
The property ParentID does not exist on object<SilverStripe\ORM\DataObject>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
29
		try {
30
			$obj2->write();
31
		}
32
		catch (ValidationException $e) {
33
			$this->assertContains('Infinite loop found within the "HierarchyTest_Object" hierarchy', $e->getMessage());
34
			return;
35
		}
36
37
		$this->fail('Failed to prevent infinite loop in hierarchy.');
38
	}
39
40
	/**
41
	 * Test Hierarchy::AllHistoricalChildren().
42
	 */
43
	public function testAllHistoricalChildren() {
44
		// Delete some objs
45
		$this->objFromFixture('HierarchyTest_Object', 'obj2b')->delete();
46
		$this->objFromFixture('HierarchyTest_Object', 'obj3a')->delete();
47
		$this->objFromFixture('HierarchyTest_Object', 'obj3')->delete();
48
49
		// Check that obj1-3 appear at the top level of the AllHistoricalChildren tree
50
		$this->assertEquals(array("Obj 1", "Obj 2", "Obj 3"),
51
			singleton('HierarchyTest_Object')->AllHistoricalChildren()->column('Title'));
52
53
		// Check numHistoricalChildren
54
		$this->assertEquals(3, singleton('HierarchyTest_Object')->numHistoricalChildren());
55
56
		// Check that both obj 2 children are returned
57
		$obj2 = $this->objFromFixture('HierarchyTest_Object', 'obj2');
58
		$this->assertEquals(array("Obj 2a", "Obj 2b"),
59
			$obj2->AllHistoricalChildren()->column('Title'));
60
61
		// Check numHistoricalChildren
62
		$this->assertEquals(2, $obj2->numHistoricalChildren());
63
64
65
		// Obj 3 has been deleted; let's bring it back from the grave
66
		$obj3 = Versioned::get_including_deleted("HierarchyTest_Object", "\"Title\" = 'Obj 3'")->First();
67
68
		// Check that all obj 3 children are returned
69
		$this->assertEquals(array("Obj 3a", "Obj 3b", "Obj 3c", "Obj 3d"),
70
			$obj3->AllHistoricalChildren()->column('Title'));
71
72
		// Check numHistoricalChildren
73
		$this->assertEquals(4, $obj3->numHistoricalChildren());
74
75
	}
76
77
	/**
78
	 * Test that you can call Hierarchy::markExpanded/Unexpanded/Open() on a obj, and that
79
	 * calling Hierarchy::isMarked() on a different instance of that object will return true.
80
	 */
81
	public function testItemMarkingIsntRestrictedToSpecificInstance() {
82
		// Mark a few objs
83
		$this->objFromFixture('HierarchyTest_Object', 'obj2')->markExpanded();
84
		$this->objFromFixture('HierarchyTest_Object', 'obj2a')->markExpanded();
85
		$this->objFromFixture('HierarchyTest_Object', 'obj2b')->markExpanded();
86
		$this->objFromFixture('HierarchyTest_Object', 'obj3')->markUnexpanded();
87
88
		// Query some objs in a different context and check their m
89
		$objs = DataObject::get("HierarchyTest_Object", '', '"ID" ASC');
90
		$marked = $expanded = array();
91
		foreach($objs as $obj) {
92
			if($obj->isMarked()) $marked[] = $obj->Title;
93
			if($obj->isExpanded()) $expanded[] = $obj->Title;
94
		}
95
96
		$this->assertEquals(array('Obj 2', 'Obj 3', 'Obj 2a', 'Obj 2b'), $marked);
97
		$this->assertEquals(array('Obj 2', 'Obj 2a', 'Obj 2b'), $expanded);
98
	}
99
100
	public function testNumChildren() {
101
		$this->assertEquals($this->objFromFixture('HierarchyTest_Object', 'obj1')->numChildren(), 0);
102
		$this->assertEquals($this->objFromFixture('HierarchyTest_Object', 'obj2')->numChildren(), 2);
103
		$this->assertEquals($this->objFromFixture('HierarchyTest_Object', 'obj3')->numChildren(), 4);
104
		$this->assertEquals($this->objFromFixture('HierarchyTest_Object', 'obj2a')->numChildren(), 2);
105
		$this->assertEquals($this->objFromFixture('HierarchyTest_Object', 'obj2b')->numChildren(), 0);
106
		$this->assertEquals($this->objFromFixture('HierarchyTest_Object', 'obj3a')->numChildren(), 2);
107
		$this->assertEquals($this->objFromFixture('HierarchyTest_Object', 'obj3d')->numChildren(), 0);
108
109
		$obj1 = $this->objFromFixture('HierarchyTest_Object', 'obj1');
110
		$this->assertEquals($obj1->numChildren(), 0);
111
		$obj1Child1 = new HierarchyTest_Object();
112
		$obj1Child1->ParentID = $obj1->ID;
0 ignored issues
show
Documentation introduced by
The property ParentID does not exist on object<HierarchyTest_Object>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
113
		$obj1Child1->write();
114
		$this->assertEquals($obj1->numChildren(false), 1,
115
			'numChildren() caching can be disabled through method parameter'
116
		);
117
		$obj1Child2 = new HierarchyTest_Object();
118
		$obj1Child2->ParentID = $obj1->ID;
0 ignored issues
show
Documentation introduced by
The property ParentID does not exist on object<HierarchyTest_Object>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
119
		$obj1Child2->write();
120
		$obj1->flushCache();
121
		$this->assertEquals($obj1->numChildren(), 2,
122
			'numChildren() caching can be disabled by flushCache()'
123
		);
124
	}
125
126
	public function testLoadDescendantIDListIntoArray() {
127
		$obj2 = $this->objFromFixture('HierarchyTest_Object', 'obj2');
128
		$obj2a = $this->objFromFixture('HierarchyTest_Object', 'obj2a');
129
		$obj2b = $this->objFromFixture('HierarchyTest_Object', 'obj2b');
130
		$obj2aa = $this->objFromFixture('HierarchyTest_Object', 'obj2aa');
131
		$obj2ab = $this->objFromFixture('HierarchyTest_Object', 'obj2ab');
132
133
		$obj2IdList = $obj2->getDescendantIDList();
134
		$obj2aIdList = $obj2a->getDescendantIDList();
135
136
		$this->assertContains($obj2a->ID, $obj2IdList);
137
		$this->assertContains($obj2b->ID, $obj2IdList);
138
		$this->assertContains($obj2aa->ID, $obj2IdList);
139
		$this->assertContains($obj2ab->ID, $obj2IdList);
140
		$this->assertEquals(4, count($obj2IdList));
141
142
		$this->assertContains($obj2aa->ID, $obj2aIdList);
143
		$this->assertContains($obj2ab->ID, $obj2aIdList);
144
		$this->assertEquals(2, count($obj2aIdList));
145
	}
146
147
	/**
148
	 * The "only deleted from stage" argument to liveChildren() should exclude
149
	 * any page that has been moved to another location on the stage site
150
	 */
151
	public function testLiveChildrenOnlyDeletedFromStage() {
152
		$obj1 = $this->objFromFixture('HierarchyTest_Object', 'obj1');
153
		$obj2 = $this->objFromFixture('HierarchyTest_Object', 'obj2');
154
		$obj2a = $this->objFromFixture('HierarchyTest_Object', 'obj2a');
155
		$obj2b = $this->objFromFixture('HierarchyTest_Object', 'obj2b');
156
157
		// Get a published set of objects for our fixture
158
		$obj1->copyVersionToStage(Versioned::DRAFT, Versioned::LIVE);
159
		$obj2->copyVersionToStage(Versioned::DRAFT, Versioned::LIVE);
160
		$obj2a->copyVersionToStage(Versioned::DRAFT, Versioned::LIVE);
161
		$obj2b->copyVersionToStage(Versioned::DRAFT, Versioned::LIVE);
162
163
		// Then delete 2a from stage and move 2b to a sub-node of 1.
164
		$obj2a->delete();
165
		$obj2b->ParentID = $obj1->ID;
0 ignored issues
show
Documentation introduced by
The property ParentID does not exist on object<SilverStripe\ORM\DataObject>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
166
		$obj2b->write();
167
168
		// Get live children, excluding pages that have been moved on the stage site
169
		$children = $obj2->liveChildren(true, true)->column("Title");
170
171
		// 2a has been deleted from stage and should be shown
172
		$this->assertContains("Obj 2a", $children);
173
174
		// 2b has merely been moved to a different parent and so shouldn't be shown
175
		$this->assertNotContains("Obj 2b", $children);
176
	}
177
178
	public function testBreadcrumbs() {
179
		$obj1 = $this->objFromFixture('HierarchyTest_Object', 'obj1');
180
		$obj2 = $this->objFromFixture('HierarchyTest_Object', 'obj2');
181
		$obj2a = $this->objFromFixture('HierarchyTest_Object', 'obj2a');
182
		$obj2aa = $this->objFromFixture('HierarchyTest_Object', 'obj2aa');
183
184
		$this->assertEquals('Obj 1', $obj1->getBreadcrumbs());
185
		$this->assertEquals('Obj 2 &raquo; Obj 2a', $obj2a->getBreadcrumbs());
186
		$this->assertEquals('Obj 2 &raquo; Obj 2a &raquo; Obj 2aa', $obj2aa->getBreadcrumbs());
187
	}
188
189
	/**
190
	 * @covers Hierarchy::markChildren()
191
	 */
192
	public function testMarkChildrenDoesntUnmarkPreviouslyMarked() {
193
		$obj3 = $this->objFromFixture('HierarchyTest_Object', 'obj3');
194
		$obj3aa = $this->objFromFixture('HierarchyTest_Object', 'obj3aa');
195
		$obj3ba = $this->objFromFixture('HierarchyTest_Object', 'obj3ba');
196
		$obj3ca = $this->objFromFixture('HierarchyTest_Object', 'obj3ca');
197
198
		$obj3->markPartialTree();
199
		$obj3->markToExpose($obj3aa);
200
		$obj3->markToExpose($obj3ba);
201
		$obj3->markToExpose($obj3ca);
202
203
		$expected = <<<EOT
204
<ul>
205
<li>Obj 3a
206
<ul>
207
<li>Obj 3aa
208
</li>
209
<li>Obj 3ab
210
</li>
211
</ul>
212
</li>
213
<li>Obj 3b
214
<ul>
215
<li>Obj 3ba
216
</li>
217
<li>Obj 3bb
218
</li>
219
</ul>
220
</li>
221
<li>Obj 3c
222
<ul>
223
<li>Obj 3c
224
</li>
225
</ul>
226
</li>
227
<li>Obj 3d
228
</li>
229
</ul>
230
231
EOT;
232
233
		$this->assertSame($expected, $obj3->getChildrenAsUL());
234
	}
235
236
	public function testGetChildrenAsUL() {
237
		$obj1 = $this->objFromFixture('HierarchyTest_Object', 'obj1');
238
		$obj2 = $this->objFromFixture('HierarchyTest_Object', 'obj2');
239
		$obj2a = $this->objFromFixture('HierarchyTest_Object', 'obj2a');
240
		$obj2aa = $this->objFromFixture('HierarchyTest_Object', 'obj2aa');
241
242
		$nodeCountThreshold = 30;
243
244
		$root = new HierarchyTest_Object();
245
		$root->markPartialTree($nodeCountThreshold);
246
		$html = $root->getChildrenAsUL(
247
			"",
248
			'"<li id=\"" . $child->ID . "\">" . $child->Title',
249
			null,
250
			false,
251
			"AllChildrenIncludingDeleted",
252
			"numChildren",
253
			true,  // rootCall
254
			$nodeCountThreshold
255
		);
256
		$this->assertTreeContains($html, array($obj2),
257
			'Contains root elements'
258
		);
259
		$this->assertTreeContains($html, array($obj2, $obj2a),
260
			'Contains child elements (in correct nesting)'
261
		);
262
		$this->assertTreeContains($html, array($obj2, $obj2a, $obj2aa),
263
			'Contains grandchild elements (in correct nesting)'
264
		);
265
	}
266
267
	public function testGetChildrenAsULMinNodeCount() {
268
		$obj1 = $this->objFromFixture('HierarchyTest_Object', 'obj1');
269
		$obj2 = $this->objFromFixture('HierarchyTest_Object', 'obj2');
270
		$obj2a = $this->objFromFixture('HierarchyTest_Object', 'obj2a');
271
272
		// Set low enough that it should be fulfilled by root only elements
273
		$nodeCountThreshold = 3;
274
275
		$root = new HierarchyTest_Object();
276
		$root->markPartialTree($nodeCountThreshold);
277
		$html = $root->getChildrenAsUL(
278
			"",
279
			'"<li id=\"" . $child->ID . "\">" . $child->Title',
280
			null,
281
			false,
282
			"AllChildrenIncludingDeleted",
283
			"numChildren",
284
			true,
285
			$nodeCountThreshold
286
		);
287
		$this->assertTreeContains($html, array($obj1),
288
			'Contains root elements'
289
		);
290
		$this->assertTreeContains($html, array($obj2),
291
			'Contains root elements'
292
		);
293
		$this->assertTreeNotContains($html, array($obj2, $obj2a),
294
			'Does not contains child elements because they exceed minNodeCount'
295
		);
296
	}
297
298
	public function testGetChildrenAsULMinNodeCountWithMarkToExpose() {
299
		$obj2 = $this->objFromFixture('HierarchyTest_Object', 'obj2');
300
		$obj2a = $this->objFromFixture('HierarchyTest_Object', 'obj2a');
301
		$obj2aa = $this->objFromFixture('HierarchyTest_Object', 'obj2aa');
302
303
		// Set low enough that it should be fulfilled by root only elements
304
		$nodeCountThreshold = 3;
305
306
		$root = new HierarchyTest_Object();
307
		$root->markPartialTree($nodeCountThreshold);
308
309
		// Mark certain node which should be included regardless of minNodeCount restrictions
310
		$root->markToExpose($obj2aa);
311
312
		$html = $root->getChildrenAsUL(
313
			"",
314
			'"<li id=\"" . $child->ID . "\">" . $child->Title',
315
			null,
316
			false,
317
			"AllChildrenIncludingDeleted",
318
			"numChildren",
319
			true,
320
			$nodeCountThreshold
321
		);
322
		$this->assertTreeContains($html, array($obj2),
323
			'Contains root elements'
324
		);
325
		$this->assertTreeContains($html, array($obj2, $obj2a, $obj2aa),
326
			'Does contain marked children nodes regardless of configured threshold'
327
		);
328
	}
329
330
	public function testGetChildrenAsULMinNodeCountWithFilters() {
331
		$obj1 = $this->objFromFixture('HierarchyTest_Object', 'obj1');
332
		$obj2 = $this->objFromFixture('HierarchyTest_Object', 'obj2');
333
		$obj2a = $this->objFromFixture('HierarchyTest_Object', 'obj2a');
334
		$obj2aa = $this->objFromFixture('HierarchyTest_Object', 'obj2aa');
335
336
		// Set low enough that it should fit all search matches without lazy loading
337
		$nodeCountThreshold = 3;
338
339
		$root = new HierarchyTest_Object();
340
341
		// Includes nodes by filter regardless of minNodeCount restrictions
342
		$root->setMarkingFilterFunction(function($record) use($obj2, $obj2a, $obj2aa) {
343
			// Results need to include parent hierarchy, even if we just want to
344
			// match the innermost node.
345
			return in_array($record->ID, array($obj2->ID, $obj2a->ID, $obj2aa->ID));
346
		});
347
		$root->markPartialTree($nodeCountThreshold);
348
349
		$html = $root->getChildrenAsUL(
350
			"",
351
			'"<li id=\"" . $child->ID . "\">" . $child->Title',
352
			null,
353
			true, // limit to marked
354
			"AllChildrenIncludingDeleted",
355
			"numChildren",
356
			true,
357
			$nodeCountThreshold
358
		);
359
		$this->assertTreeNotContains($html, array($obj1),
360
			'Does not contain root elements which dont match the filter'
361
		);
362
		$this->assertTreeContains($html, array($obj2, $obj2a, $obj2aa),
363
			'Contains non-root elements which match the filter'
364
		);
365
	}
366
367
	public function testGetChildrenAsULHardLimitsNodes() {
368
		$obj1 = $this->objFromFixture('HierarchyTest_Object', 'obj1');
369
		$obj2 = $this->objFromFixture('HierarchyTest_Object', 'obj2');
370
		$obj2a = $this->objFromFixture('HierarchyTest_Object', 'obj2a');
371
		$obj2aa = $this->objFromFixture('HierarchyTest_Object', 'obj2aa');
372
373
		// Set low enough that it should fit all search matches without lazy loading
374
		$nodeCountThreshold = 3;
375
376
		$root = new HierarchyTest_Object();
377
378
		// Includes nodes by filter regardless of minNodeCount restrictions
379
		$root->setMarkingFilterFunction(function($record) use($obj2, $obj2a, $obj2aa) {
380
			// Results need to include parent hierarchy, even if we just want to
381
			// match the innermost node.
382
			return in_array($record->ID, array($obj2->ID, $obj2a->ID, $obj2aa->ID));
383
		});
384
		$root->markPartialTree($nodeCountThreshold);
385
386
		$html = $root->getChildrenAsUL(
387
			"",
388
			'"<li id=\"" . $child->ID . "\">" . $child->Title',
389
			null,
390
			true, // limit to marked
391
			"AllChildrenIncludingDeleted",
392
			"numChildren",
393
			true,
394
			$nodeCountThreshold
395
		);
396
		$this->assertTreeNotContains($html, array($obj1),
397
			'Does not contain root elements which dont match the filter'
398
		);
399
		$this->assertTreeContains($html, array($obj2, $obj2a, $obj2aa),
400
			'Contains non-root elements which match the filter'
401
		);
402
	}
403
404
	public function testGetChildrenAsULNodeThresholdLeaf() {
405
		$obj1 = $this->objFromFixture('HierarchyTest_Object', 'obj1');
406
		$obj2 = $this->objFromFixture('HierarchyTest_Object', 'obj2');
407
		$obj2a = $this->objFromFixture('HierarchyTest_Object', 'obj2a');
408
		$obj3 = $this->objFromFixture('HierarchyTest_Object', 'obj3');
409
		$obj3a = $this->objFromFixture('HierarchyTest_Object', 'obj3a');
410
411
		$nodeCountThreshold = 99;
412
413
		$root = new HierarchyTest_Object();
414
		$root->markPartialTree($nodeCountThreshold);
415
		$nodeCountCallback = function($parent, $numChildren) {
416
			// Set low enough that it the fixture structure should exceed it
417
			if($parent->ID && $numChildren > 2) {
418
				return '<span class="exceeded">Exceeded!</span>';
419
			}
420
		};
421
422
		$html = $root->getChildrenAsUL(
423
			"",
424
			'"<li id=\"" . $child->ID . "\">" . $child->Title',
425
			null,
426
			true, // limit to marked
427
			"AllChildrenIncludingDeleted",
428
			"numChildren",
429
			true,
430
			$nodeCountThreshold,
431
			$nodeCountCallback
432
		);
433
		$this->assertTreeContains($html, array($obj1),
434
			'Does contain root elements regardless of count'
435
		);
436
		$this->assertTreeContains($html, array($obj3),
437
			'Does contain root elements regardless of count'
438
		);
439
		$this->assertTreeContains($html, array($obj2, $obj2a),
440
			'Contains children which do not exceed threshold'
441
		);
442
		$this->assertTreeNotContains($html, array($obj3, $obj3a),
443
			'Does not contain children which exceed threshold'
444
		);
445
	}
446
447
	/**
448
	 * This test checks that deleted ('archived') child pages don't set a css class on the parent
449
	 * node that makes it look like it has children
450
	 */
451
	public function testGetChildrenAsULNodeDeletedOnLive() {
452
		$obj2 = $this->objFromFixture('HierarchyTest_Object', 'obj2');
453
		$obj2a = $this->objFromFixture('HierarchyTest_Object', 'obj2a');
454
		$obj2aa = $this->objFromFixture('HierarchyTest_Object', 'obj2aa');
455
		$obj2ab = $this->objFromFixture('HierarchyTest_Object', 'obj2b');
456
457
		// delete all children under obj2
458
		$obj2a->delete();
459
		$obj2aa->delete();
460
		$obj2ab->delete();
461
		// Don't pre-load all children
462
		$nodeCountThreshold = 1;
463
464
		$childrenMethod = 'AllChildren';
465
		$numChildrenMethod = 'numChildren';
466
467
		$root = new HierarchyTest_Object();
468
		$root->markPartialTree($nodeCountThreshold, null, $childrenMethod, $numChildrenMethod);
469
470
		// As in LeftAndMain::getSiteTreeFor() but simpler and more to the point for testing purposes
471
		$titleFn = function(&$child, $numChildrenMethod="") {
472
			return '<li class="' . $child->markingClasses($numChildrenMethod).
473
				'" id="' . $child->ID . '">"' . $child->Title;
474
		};
475
476
		$html = $root->getChildrenAsUL(
477
			"",
478
			$titleFn,
479
			null,
480
			true, // limit to marked
481
			$childrenMethod,
482
			$numChildrenMethod,
483
			true,
484
			$nodeCountThreshold
485
		);
486
487
		// Get the class attribute from the $obj2 node in the sitetree, class 'jstree-leaf' means it's a leaf node
488
		$nodeClass = $this->getNodeClassFromTree($html, $obj2);
489
		$this->assertEquals('jstree-leaf closed', $nodeClass, 'object2 should not have children in the sitetree');
490
	}
491
492
	/**
493
	 * This test checks that deleted ('archived') child pages _do_ set a css class on the parent
494
	 * node that makes it look like it has children when getting all children including deleted
495
	 */
496
	public function testGetChildrenAsULNodeDeletedOnStage() {
497
		$obj2 = $this->objFromFixture('HierarchyTest_Object', 'obj2');
498
		$obj2a = $this->objFromFixture('HierarchyTest_Object', 'obj2a');
499
		$obj2aa = $this->objFromFixture('HierarchyTest_Object', 'obj2aa');
500
		$obj2ab = $this->objFromFixture('HierarchyTest_Object', 'obj2b');
501
502
		// delete all children under obj2
503
		$obj2a->delete();
504
		$obj2aa->delete();
505
		$obj2ab->delete();
506
		// Don't pre-load all children
507
		$nodeCountThreshold = 1;
508
509
		$childrenMethod = 'AllChildrenIncludingDeleted';
510
		$numChildrenMethod = 'numHistoricalChildren';
511
512
		$root = new HierarchyTest_Object();
513
		$root->markPartialTree($nodeCountThreshold, null, $childrenMethod, $numChildrenMethod);
514
515
		// As in LeftAndMain::getSiteTreeFor() but simpler and more to the point for testing purposes
516
		$titleFn = function(&$child, $numChildrenMethod="") {
517
			return '<li class="' . $child->markingClasses($numChildrenMethod).
518
				'" id="' . $child->ID . '">"' . $child->Title;
519
		};
520
521
		$html = $root->getChildrenAsUL(
522
			"",
523
			$titleFn,
524
			null,
525
			true, // limit to marked
526
			$childrenMethod,
527
			$numChildrenMethod,
528
			true,
529
			$nodeCountThreshold
530
		);
531
532
		// Get the class attribute from the $obj2 node in the sitetree
533
		$nodeClass = $this->getNodeClassFromTree($html, $obj2);
534
		// Object2 can now be expanded
535
		$this->assertEquals('unexpanded jstree-closed closed', $nodeClass, 'obj2 should have children in the sitetree');
536
	}
537
538
	public function testNoHideFromHeirarchy() {
539
		$obj4 = $this->objFromFixture('HierarchyHideTest_Object', 'obj4');
540
		$obj4->publish("Stage", "Live");
541
542
		foreach($obj4->stageChildren() as $child) {
543
			$child->publish("Stage", "Live");
544
		}
545
		$this->assertEquals($obj4->stageChildren()->Count(), 2);
546
		$this->assertEquals($obj4->liveChildren()->Count(), 2);
547
	}
548
549
	public function testHideFromHeirarchy() {
550
		HierarchyHideTest_Object::config()->hide_from_hierarchy = array('HierarchyHideTest_SubObject');
0 ignored issues
show
Documentation introduced by
The property hide_from_hierarchy does not exist on object<Config_ForClass>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
551
		$obj4 = $this->objFromFixture('HierarchyHideTest_Object', 'obj4');
552
		$obj4->publish("Stage", "Live");
553
554
		// load without using stage children otherwise it'll bbe filtered before it's publish
555
		// we need to publish all of them, and expect liveChildren to return some.
556
		$children = HierarchyHideTest_Object::get()
557
			->filter('ParentID', (int)$obj4->ID)
558
			->exclude('ID', (int)$obj4->ID);
559
560
		foreach($children as $child) {
561
			$child->publish("Stage", "Live");
562
		}
563
		$this->assertEquals($obj4->stageChildren()->Count(), 1);
564
		$this->assertEquals($obj4->liveChildren()->Count(), 1);
565
	}
566
567
	/**
568
	 * @param String $html  [description]
569
	 * @param array $nodes Breadcrumb path as array
570
	 * @param String $message
571
	 */
572
	protected function assertTreeContains($html, $nodes, $message = null) {
573
		$parser = new CSSContentParser($html);
574
		$xpath = '/';
575
		foreach($nodes as $node) $xpath .= '/ul/li[@id="' . $node->ID . '"]';
576
		$match = $parser->getByXpath($xpath);
577
		self::assertThat((bool)$match, self::isTrue(), $message);
578
	}
579
580
	/**
581
	 * @param String $html  [description]
582
	 * @param array $nodes Breadcrumb path as array
583
	 * @param String $message
584
	 */
585
	protected function assertTreeNotContains($html, $nodes, $message = null) {
586
		$parser = new CSSContentParser($html);
587
		$xpath = '/';
588
		foreach($nodes as $node) $xpath .= '/ul/li[@id="' . $node->ID . '"]';
589
		$match = $parser->getByXpath($xpath);
590
		self::assertThat((bool)$match, self::isFalse(), $message);
591
	}
592
593
	/**
594
	 * Get the HTML class attribute from a node in the sitetree
595
	 *
596
	 * @param $html
597
	 * @param $node
598
	 * @return string
599
	 */
600
	protected function getNodeClassFromTree($html, $node) {
601
		$parser = new CSSContentParser($html);
602
		$xpath = '//ul/li[@id="' . $node->ID . '"]';
603
		$object = $parser->getByXpath($xpath);
604
605
		foreach($object[0]->attributes() as $key => $attr) {
606
			if($key == 'class') {
607
				return (string)$attr;
608
			}
609
		}
610
		return '';
611
	}
612
}
613
614
class HierarchyTest_Object extends DataObject implements TestOnly {
615
	private static $db = array(
616
		'Title' => 'Varchar'
617
	);
618
619
	private static $extensions = array(
620
		'SilverStripe\\ORM\\Hierarchy\\Hierarchy',
621
		'SilverStripe\\ORM\\Versioning\\Versioned',
622
	);
623
624
	private static $default_sort = 'Title ASC';
625
626
	public function cmstreeclasses() {
627
		return $this->markingClasses();
628
	}
629
}
630
631
class HierarchyHideTest_Object extends DataObject implements TestOnly {
632
	private static $db = array(
633
		'Title' => 'Varchar'
634
	);
635
636
	private static $extensions = array(
637
		'SilverStripe\\ORM\\Hierarchy\\Hierarchy',
638
		"SilverStripe\\ORM\\Versioning\\Versioned('Stage', 'Live')",
639
	);
640
641
	public function cmstreeclasses() {
642
		return $this->markingClasses();
643
	}
644
}
645
646
class HierarchyHideTest_SubObject extends HierarchyHideTest_Object {
647
648
}
649