Completed
Push — master ( 3f1f9d...ab54c8 )
by Ingo
08:52
created

testGetChildrenAsULMinNodeCountWithMarkToExpose()   B

Complexity

Conditions 1
Paths 1

Size

Total Lines 26
Code Lines 18

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 18
nc 1
nop 0
dl 0
loc 26
rs 8.8571
c 0
b 0
f 0
1
<?php
2
3
namespace SilverStripe\ORM\Tests;
4
5
use DOMDocument;
6
use SilverStripe\Dev\CSSContentParser;
7
use SilverStripe\Dev\SapphireTest;
8
use SilverStripe\ORM\DataObject;
9
use SilverStripe\ORM\Hierarchy\MarkedSet;
10
use SilverStripe\Versioned\Versioned;
11
12
/**
13
 * Test set of marked Hierarchy-extended DataObjects
14
 */
15
class MarkedSetTest extends SapphireTest
16
{
17
    protected static $fixture_file = 'HierarchyTest.yml';
18
19
    protected static $extra_dataobjects = array(
20
        HierarchyTest\TestObject::class,
21
        HierarchyTest\HideTestObject::class,
22
        HierarchyTest\HideTestSubObject::class,
23
    );
24
25
    protected static function getExtraDataObjects()
26
    {
27
        // Prevent setup breaking if versioned module absent
28
        if (class_exists(Versioned::class)) {
29
            return parent::getExtraDataObjects();
30
        }
31
        return [];
32
    }
33
34
    public function setUp()
35
    {
36
        parent::setUp();
37
38
        // Note: Soft support for versioned module optionality
39
        if (!class_exists(Versioned::class)) {
40
            $this->markTestSkipped('MarkedSetTest requires the Versioned extension');
41
        }
42
    }
43
44
45
    /**
46
     * Test that you can call MarkedSet::markExpanded/Unexpanded/Open() on a obj, and that
47
     * calling MarkedSet::isMarked() on a different instance of that object will return true.
48
     */
49
    public function testItemMarkingIsntRestrictedToSpecificInstance()
50
    {
51
        // Build new object
52
        $set = new MarkedSet(HierarchyTest\TestObject::singleton());
53
54
        // Mark a few objs
55
        $set->markExpanded($this->objFromFixture(HierarchyTest\TestObject::class, 'obj2'));
0 ignored issues
show
Bug introduced by
It seems like $this->objFromFixture(\S...tObject::class, 'obj2') can be null; however, markExpanded() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
56
        $set->markExpanded($this->objFromFixture(HierarchyTest\TestObject::class, 'obj2a'));
0 ignored issues
show
Bug introduced by
It seems like $this->objFromFixture(\S...Object::class, 'obj2a') can be null; however, markExpanded() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
57
        $set->markExpanded($this->objFromFixture(HierarchyTest\TestObject::class, 'obj2b'));
0 ignored issues
show
Bug introduced by
It seems like $this->objFromFixture(\S...Object::class, 'obj2b') can be null; however, markExpanded() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
58
        $set->markUnexpanded($this->objFromFixture(HierarchyTest\TestObject::class, 'obj3'));
0 ignored issues
show
Bug introduced by
It seems like $this->objFromFixture(\S...tObject::class, 'obj3') can be null; however, markUnexpanded() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
59
60
        // Query some objs in a different context and check their m
61
        $objs = DataObject::get(HierarchyTest\TestObject::class, '', '"ID" ASC');
62
        $marked = $expanded = array();
63
        foreach ($objs as $obj) {
64
            if ($set->isMarked($obj)) {
65
                $marked[] = $obj->Title;
66
            }
67
            if ($set->isExpanded($obj)) {
68
                $expanded[] = $obj->Title;
69
            }
70
        }
71
        $this->assertEquals(array('Obj 2', 'Obj 3', 'Obj 2a', 'Obj 2b'), $marked);
72
        $this->assertEquals(array('Obj 2', 'Obj 2a', 'Obj 2b'), $expanded);
73
    }
74
75
    /**
76
     * @covers \SilverStripe\ORM\Hierarchy\MarkedSet::markChildren()
77
     */
78
    public function testMarkChildrenDoesntUnmarkPreviouslyMarked()
79
    {
80
        $obj3 = $this->objFromFixture(HierarchyTest\TestObject::class, 'obj3');
81
        $obj3aa = $this->objFromFixture(HierarchyTest\TestObject::class, 'obj3aa');
82
        $obj3ba = $this->objFromFixture(HierarchyTest\TestObject::class, 'obj3ba');
83
        $obj3ca = $this->objFromFixture(HierarchyTest\TestObject::class, 'obj3ca');
84
85
        $set = new MarkedSet($obj3);
0 ignored issues
show
Bug introduced by
It seems like $obj3 defined by $this->objFromFixture(\S...tObject::class, 'obj3') on line 80 can be null; however, SilverStripe\ORM\Hierarc...arkedSet::__construct() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
86
        $set->markPartialTree();
87
        $set->markToExpose($obj3aa);
0 ignored issues
show
Bug introduced by
It seems like $obj3aa defined by $this->objFromFixture(\S...bject::class, 'obj3aa') on line 81 can be null; however, SilverStripe\ORM\Hierarc...rkedSet::markToExpose() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
88
        $set->markToExpose($obj3ba);
0 ignored issues
show
Bug introduced by
It seems like $obj3ba defined by $this->objFromFixture(\S...bject::class, 'obj3ba') on line 82 can be null; however, SilverStripe\ORM\Hierarc...rkedSet::markToExpose() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
89
        $set->markToExpose($obj3ca);
0 ignored issues
show
Bug introduced by
It seems like $obj3ca defined by $this->objFromFixture(\S...bject::class, 'obj3ca') on line 83 can be null; however, SilverStripe\ORM\Hierarc...rkedSet::markToExpose() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
90
91
        $expected = <<<EOT
92
<ul>
93
    <li>Obj 3a
94
        <ul>
95
            <li>Obj 3aa
96
            </li>
97
            <li>Obj 3ab
98
            </li>
99
        </ul>
100
    </li>
101
    <li>Obj 3b
102
        <ul>
103
            <li>Obj 3ba
104
            </li>
105
            <li>Obj 3bb
106
            </li>
107
        </ul>
108
    </li>
109
    <li>Obj 3c
110
        <ul>
111
            <li>Obj 3c
112
            </li>
113
        </ul>
114
    </li>
115
    <li>Obj 3d
116
    </li>
117
</ul>
118
119
EOT;
120
121
        $this->assertHTMLSame($expected, $set->renderChildren());
122
    }
123
124
    public function testGetChildrenCustomTemplate()
125
    {
126
        $obj2 = $this->objFromFixture(HierarchyTest\TestObject::class, 'obj2');
127
        $obj2a = $this->objFromFixture(HierarchyTest\TestObject::class, 'obj2a');
128
        $obj2aa = $this->objFromFixture(HierarchyTest\TestObject::class, 'obj2aa');
129
130
        // Render marked tree
131
        $set = new MarkedSet(HierarchyTest\TestObject::singleton(), 'AllChildrenIncludingDeleted', 'numChildren', 30);
132
        $set->markPartialTree();
133
        $template = __DIR__ . '/HierarchyTest/MarkedSetTest_HTML.ss';
134
        $html = $set->renderChildren($template);
135
136
        $this->assertTreeContains(
137
            $html,
138
            array($obj2),
139
            'Contains root elements'
140
        );
141
        $this->assertTreeContains(
142
            $html,
143
            array($obj2, $obj2a),
144
            'Contains child elements (in correct nesting)'
145
        );
146
        $this->assertTreeContains(
147
            $html,
148
            array($obj2, $obj2a, $obj2aa),
149
            'Contains grandchild elements (in correct nesting)'
150
        );
151
    }
152
153
    public function testGetChildrenAsULMinNodeCount()
154
    {
155
        $obj1 = $this->objFromFixture(HierarchyTest\TestObject::class, 'obj1');
156
        $obj2 = $this->objFromFixture(HierarchyTest\TestObject::class, 'obj2');
157
        $obj2a = $this->objFromFixture(HierarchyTest\TestObject::class, 'obj2a');
158
159
        // Render marked tree
160
        $set = new MarkedSet(HierarchyTest\TestObject::singleton(), 'AllChildrenIncludingDeleted', 'numChildren');
161
        $set->setNodeCountThreshold(3); // Set low enough that it should be fulfilled by root only elements
162
        $set->markPartialTree();
163
        $template = __DIR__ . '/HierarchyTest/MarkedSetTest_HTML.ss';
164
        $html = $set->renderChildren($template);
165
166
        $this->assertTreeContains(
167
            $html,
168
            array($obj1),
169
            'Contains root elements'
170
        );
171
        $this->assertTreeContains(
172
            $html,
173
            array($obj2),
174
            'Contains root elements'
175
        );
176
        $this->assertTreeNotContains(
177
            $html,
178
            array($obj2, $obj2a),
179
            'Does not contains child elements because they exceed minNodeCount'
180
        );
181
    }
182
183
    public function testGetChildrenAsULMinNodeCountWithMarkToExpose()
184
    {
185
        $obj2 = $this->objFromFixture(HierarchyTest\TestObject::class, 'obj2');
186
        $obj2a = $this->objFromFixture(HierarchyTest\TestObject::class, 'obj2a');
187
        $obj2aa = $this->objFromFixture(HierarchyTest\TestObject::class, 'obj2aa');
188
189
        // Render marked tree
190
        $set = new MarkedSet(HierarchyTest\TestObject::singleton(), 'AllChildrenIncludingDeleted', 'numChildren');
191
        $set->setNodeCountThreshold(3); // Set low enough that it should be fulfilled by root only elements
192
        $set->markPartialTree();
193
        // Mark certain node which should be included regardless of minNodeCount restrictions
194
        $set->markToExpose($obj2aa);
0 ignored issues
show
Bug introduced by
It seems like $obj2aa defined by $this->objFromFixture(\S...bject::class, 'obj2aa') on line 187 can be null; however, SilverStripe\ORM\Hierarc...rkedSet::markToExpose() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
195
        $template = __DIR__ . '/HierarchyTest/MarkedSetTest_HTML.ss';
196
        $html = $set->renderChildren($template);
197
198
        $this->assertTreeContains(
199
            $html,
200
            array($obj2),
201
            'Contains root elements'
202
        );
203
        $this->assertTreeContains(
204
            $html,
205
            array($obj2, $obj2a, $obj2aa),
206
            'Does contain marked children nodes regardless of configured threshold'
207
        );
208
    }
209
210
    public function testGetChildrenAsULMinNodeCountWithFilters()
211
    {
212
        $obj1 = $this->objFromFixture(HierarchyTest\TestObject::class, 'obj1');
213
        $obj2 = $this->objFromFixture(HierarchyTest\TestObject::class, 'obj2');
214
        $obj2a = $this->objFromFixture(HierarchyTest\TestObject::class, 'obj2a');
215
        $obj2aa = $this->objFromFixture(HierarchyTest\TestObject::class, 'obj2aa');
216
217
        // Render marked tree
218
        $set = new MarkedSet(HierarchyTest\TestObject::singleton(), 'AllChildrenIncludingDeleted', 'numChildren');
219
        $set->setNodeCountThreshold(3); // Set low enough that it should be fulfilled by root only elements
220
        // Includes nodes by filter regardless of minNodeCount restrictions
221
        $set->setMarkingFilterFunction(
222
            function ($record) use ($obj2, $obj2a, $obj2aa) {
223
                // Results need to include parent hierarchy, even if we just want to
224
                // match the innermost node.
225
                return in_array($record->ID, array($obj2->ID, $obj2a->ID, $obj2aa->ID));
226
            }
227
        );
228
        $set->markPartialTree();
229
        // Mark certain node which should be included regardless of minNodeCount restrictions
230
        $template = __DIR__ . '/HierarchyTest/MarkedSetTest_HTML.ss';
231
        $html = $set->renderChildren($template);
232
233
        $this->assertTreeNotContains(
234
            $html,
235
            array($obj1),
236
            'Does not contain root elements which dont match the filter'
237
        );
238
        $this->assertTreeContains(
239
            $html,
240
            array($obj2, $obj2a, $obj2aa),
241
            'Contains non-root elements which match the filter'
242
        );
243
    }
244
245
    public function testGetChildrenAsULHardLimitsNodes()
246
    {
247
        $obj1 = $this->objFromFixture(HierarchyTest\TestObject::class, 'obj1');
248
        $obj2 = $this->objFromFixture(HierarchyTest\TestObject::class, 'obj2');
249
        $obj2a = $this->objFromFixture(HierarchyTest\TestObject::class, 'obj2a');
250
        $obj2aa = $this->objFromFixture(HierarchyTest\TestObject::class, 'obj2aa');
251
252
        // Render marked tree
253
        $set = new MarkedSet(HierarchyTest\TestObject::singleton(), 'AllChildrenIncludingDeleted', 'numChildren');
254
        $set->setNodeCountThreshold(3); // Set low enough that it should miss out one node
255
256
        // Includes nodes by filter regardless of minNodeCount restrictions
257
        $set->setMarkingFilterFunction(
258
            function ($record) use ($obj2, $obj2a, $obj2aa) {
259
                // Results need to include parent hierarchy, even if we just want to
260
                // match the innermost node.
261
                return in_array($record->ID, array($obj2->ID, $obj2a->ID, $obj2aa->ID));
262
            }
263
        );
264
        $set->markPartialTree();
265
        $template = __DIR__ . '/HierarchyTest/MarkedSetTest_HTML.ss';
266
        $html = $set->renderChildren($template);
267
268
        $this->assertTreeNotContains(
269
            $html,
270
            array($obj1, $obj2aa),
271
            'Does not contain root elements which dont match the filter or are limited'
272
        );
273
        $this->assertTreeContains(
274
            $html,
275
            array($obj2, $obj2a),
276
            'Contains non-root elements which match the filter'
277
        );
278
    }
279
280
    public function testGetChildrenAsULNodeThresholdLeaf()
281
    {
282
        $obj1 = $this->objFromFixture(HierarchyTest\TestObject::class, 'obj1');
283
        $obj2 = $this->objFromFixture(HierarchyTest\TestObject::class, 'obj2');
284
        $obj2a = $this->objFromFixture(HierarchyTest\TestObject::class, 'obj2a');
285
        $obj3 = $this->objFromFixture(HierarchyTest\TestObject::class, 'obj3');
286
        $obj3a = $this->objFromFixture(HierarchyTest\TestObject::class, 'obj3a');
287
288
        // Render marked tree
289
        $set = new MarkedSet(HierarchyTest\TestObject::singleton(), 'AllChildrenIncludingDeleted', 'numChildren');
290
        $set->setNodeCountThreshold(99);
291
        $set->setMaxChildNodes(2); // Force certain children to exceed limits
292
        $set->markPartialTree();
293
        $template = __DIR__ . '/HierarchyTest/MarkedSetTest_HTML.ss';
294
        $html = $set->renderChildren($template);
295
296
        $this->assertTreeContains(
297
            $html,
298
            array($obj1),
299
            'Does contain root elements regardless of count'
300
        );
301
        $this->assertTreeContains(
302
            $html,
303
            array($obj3),
304
            'Does contain root elements regardless of count'
305
        );
306
        $this->assertTreeContains(
307
            $html,
308
            array($obj2, $obj2a),
309
            'Contains children which do not exceed threshold'
310
        );
311
        $this->assertTreeNotContains(
312
            $html,
313
            array($obj3, $obj3a),
314
            'Does not contain children which exceed threshold'
315
        );
316
    }
317
318
    /**
319
     * This test checks that deleted ('archived') child pages don't set a css class on the parent
320
     * node that makes it look like it has children
321
     */
322
    public function testGetChildrenAsULNodeDeletedOnLive()
323
    {
324
        $obj2 = $this->objFromFixture(HierarchyTest\TestObject::class, 'obj2');
325
        $obj2a = $this->objFromFixture(HierarchyTest\TestObject::class, 'obj2a');
326
        $obj2aa = $this->objFromFixture(HierarchyTest\TestObject::class, 'obj2aa');
327
        $obj2ab = $this->objFromFixture(HierarchyTest\TestObject::class, 'obj2b');
328
329
        // delete all children under obj2
330
        $obj2a->delete();
331
        $obj2aa->delete();
332
        $obj2ab->delete();
333
334
        $set = new MarkedSet(
335
            HierarchyTest\TestObject::singleton(),
336
            'AllChildren',
337
            'numChildren'
338
        );
339
        // Don't pre-load all children
340
        $set->setNodeCountThreshold(1);
341
        $set->markPartialTree();
342
        $template = __DIR__ . '/HierarchyTest/MarkedSetTest_HTML.ss';
343
        $html = $set->renderChildren($template);
344
345
        // Get the class attribute from the $obj2 node in the sitetree, class 'jstree-leaf' means it's a leaf node
346
        $nodeClass = $this->getNodeClassFromTree($html, $obj2);
0 ignored issues
show
Bug introduced by
It seems like $obj2 defined by $this->objFromFixture(\S...tObject::class, 'obj2') on line 324 can be null; however, SilverStripe\ORM\Tests\M...:getNodeClassFromTree() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
347
        $this->assertEquals('jstree-leaf closed', $nodeClass, 'object2 should not have children in the sitetree');
348
    }
349
350
    /**
351
     * This test checks that deleted ('archived') child pages _do_ set a css class on the parent
352
     * node that makes it look like it has children when getting all children including deleted
353
     */
354
    public function testGetChildrenAsULNodeDeletedOnStage()
355
    {
356
        $obj2 = $this->objFromFixture(HierarchyTest\TestObject::class, 'obj2');
357
        $obj2a = $this->objFromFixture(HierarchyTest\TestObject::class, 'obj2a');
358
        $obj2aa = $this->objFromFixture(HierarchyTest\TestObject::class, 'obj2aa');
359
        $obj2ab = $this->objFromFixture(HierarchyTest\TestObject::class, 'obj2b');
360
361
        // delete all children under obj2
362
        $obj2a->delete();
363
        $obj2aa->delete();
364
        $obj2ab->delete();
365
366
        $set = new MarkedSet(
367
            HierarchyTest\TestObject::singleton(),
368
            'AllChildrenIncludingDeleted',
369
            'numHistoricalChildren'
370
        );
371
        // Don't pre-load all children
372
        $set->setNodeCountThreshold(1);
373
        $set->markPartialTree();
374
        $template = __DIR__ . '/HierarchyTest/MarkedSetTest_HTML.ss';
375
        $html = $set->renderChildren($template);
376
377
        // Get the class attribute from the $obj2 node in the sitetree
378
        $nodeClass = $this->getNodeClassFromTree($html, $obj2);
0 ignored issues
show
Bug introduced by
It seems like $obj2 defined by $this->objFromFixture(\S...tObject::class, 'obj2') on line 356 can be null; however, SilverStripe\ORM\Tests\M...:getNodeClassFromTree() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
379
        // Object2 can now be expanded
380
        $this->assertEquals('unexpanded jstree-closed closed', $nodeClass, 'obj2 should have children in the sitetree');
381
    }
382
383
384
    /**
385
     * @param String $html    [description]
386
     * @param array  $nodes   Breadcrumb path as array
387
     * @param String $message
388
     */
389
    protected function assertTreeContains($html, $nodes, $message = null)
390
    {
391
        $parser = new CSSContentParser($html);
392
        $xpath = '/';
393
        foreach ($nodes as $node) {
394
            $xpath .= '/ul/li[@data-id="' . $node->ID . '"]';
395
        }
396
        $match = $parser->getByXpath($xpath);
397
        self::assertThat((bool)$match, self::isTrue(), $message);
398
    }
399
400
    /**
401
     * @param String $html    [description]
402
     * @param array  $nodes   Breadcrumb path as array
403
     * @param String $message
404
     */
405
    protected function assertTreeNotContains($html, $nodes, $message = null)
406
    {
407
        $parser = new CSSContentParser($html);
408
        $xpath = '/';
409
        foreach ($nodes as $node) {
410
            $xpath .= '/ul/li[@data-id="' . $node->ID . '"]';
411
        }
412
        $match = $parser->getByXpath($xpath);
413
        self::assertThat((bool)$match, self::isFalse(), $message);
414
    }
415
416
    /**
417
     * Get the HTML class attribute from a node in the sitetree
418
     *
419
     * @param string$html
420
     * @param DataObject $node
421
     * @return string
422
     */
423
    protected function getNodeClassFromTree($html, $node)
424
    {
425
        $parser = new CSSContentParser($html);
426
        $xpath = '//ul/li[@data-id="' . $node->ID . '"]';
427
        $object = $parser->getByXpath($xpath);
428
429
        foreach ($object[0]->attributes() as $key => $attr) {
430
            if ($key == 'class') {
431
                return (string)$attr;
432
            }
433
        }
434
        return '';
435
    }
436
437
    protected function assertHTMLSame($expected, $actual, $message = '')
438
    {
439
        // Trim each line, strip empty lines
440
        $expected = implode("\n", array_filter(array_map('trim', explode("\n", $expected))));
441
        $actual = implode("\n", array_filter(array_map('trim', explode("\n", $actual))));
442
        $this->assertXmlStringEqualsXmlString($expected, $actual, $message);
443
    }
444
}
445