Passed
Push — 4 ( 5c2b28...a2ab1a )
by Maxime
13:32 queued 04:46
created

TreeDropdownFieldTest::testTreeSearchJson()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 28
Code Lines 18

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 18
nc 1
nop 0
dl 0
loc 28
rs 9.6666
c 0
b 0
f 0
1
<?php
2
3
namespace SilverStripe\Forms\Tests;
4
5
use SilverStripe\Assets\File;
6
use SilverStripe\Assets\Folder;
7
use SilverStripe\Control\Session;
8
use SilverStripe\Dev\CSSContentParser;
9
use SilverStripe\Dev\SapphireTest;
10
use SilverStripe\Control\HTTPRequest;
11
use SilverStripe\Forms\FieldList;
12
use SilverStripe\Forms\Form;
13
use SilverStripe\Forms\TreeDropdownField;
14
use SilverStripe\ORM\DataObject;
15
use SilverStripe\ORM\Tests\HierarchyTest\HierarchyOnSubclassTestObject;
16
use SilverStripe\ORM\Tests\HierarchyTest\HierarchyOnSubclassTestSubObject;
17
use SilverStripe\ORM\Tests\HierarchyTest\TestObject;
18
19
class TreeDropdownFieldTest extends SapphireTest
20
{
21
22
    protected static $fixture_file = 'TreeDropdownFieldTest.yml';
23
24
    protected static $extra_dataobjects = [
25
        TestObject::class,
26
        HierarchyOnSubclassTestObject::class,
27
        HierarchyOnSubclassTestSubObject::class,
28
    ];
29
30
    public function testSchemaStateDefaults()
31
    {
32
        $field = new TreeDropdownField('TestTree', 'Test tree', Folder::class);
33
        $folder = $this->objFromFixture(Folder::class, 'folder1-subfolder1');
34
35
        $schema = $field->getSchemaStateDefaults();
36
        $this->assertFalse(isset($schema['data']['valueObject']));
37
38
        $field->setValue($folder->ID);
39
40
        $schema = $field->getSchemaStateDefaults();
41
        $this->assertEquals($folder->ID, $schema['data']['valueObject']['id']);
42
        $this->assertTrue(isset($schema['data']['valueObject']));
43
        $this->assertFalse($schema['data']['showSelectedPath']);
44
        $this->assertEquals('', $schema['data']['valueObject']['titlePath']);
45
46
        $field->setShowSelectedPath(true);
47
        $schema = $field->getSchemaStateDefaults();
48
        $this->assertTrue($schema['data']['showSelectedPath']);
49
        $this->assertEquals(
50
            'FileTest-folder1/FileTest-folder1-subfolder1/',
51
            $schema['data']['valueObject']['titlePath']
52
        );
53
    }
54
55
    public function testTreeSearchJson()
56
    {
57
        $field = new TreeDropdownField('TestTree', 'Test tree', Folder::class);
58
59
        // case-insensitive search against keyword 'sub' for folders
60
        $request = new HTTPRequest('GET', 'url', ['search'=>'sub', 'format' => 'json']);
61
        $request->setSession(new Session([]));
62
        $response = $field->tree($request);
63
        $tree = json_decode($response->getBody(), true);
64
65
        $folder1 = $this->objFromFixture(Folder::class, 'folder1');
66
        $folder1Subfolder1 = $this->objFromFixture(Folder::class, 'folder1-subfolder1');
67
68
        $this->assertContains(
69
            $folder1->Name,
0 ignored issues
show
Bug Best Practice introduced by
The property Name does not exist on SilverStripe\ORM\DataObject. Since you implemented __get, consider adding a @property annotation.
Loading history...
70
            array_column($tree['children'], 'title'),
71
            $folder1->Name . ' is found in the json'
72
        );
73
74
        $filtered = array_filter($tree['children'], function ($entry) use ($folder1) {
75
            return $folder1->Name === $entry['title'];
0 ignored issues
show
Bug Best Practice introduced by
The property Name does not exist on SilverStripe\ORM\DataObject. Since you implemented __get, consider adding a @property annotation.
Loading history...
76
        });
77
        $folder1Tree = array_pop($filtered);
78
79
        $this->assertContains(
80
            $folder1Subfolder1->Name,
81
            array_column($folder1Tree['children'], 'title'),
82
            $folder1Subfolder1->Name . ' is found in the folder1 entry in the json'
83
        );
84
    }
85
86
    public function testTreeSearchJsonFlatlist()
87
    {
88
        $field = new TreeDropdownField('TestTree', 'Test tree', Folder::class);
89
90
        // case-insensitive search against keyword 'sub' for folders
91
        $request = new HTTPRequest('GET', 'url', ['search'=>'sub', 'format' => 'json', 'flatList' => '1']);
92
        $request->setSession(new Session([]));
93
        $response = $field->tree($request);
94
        $tree = json_decode($response->getBody(), true);
95
96
        $folder1 = $this->objFromFixture(Folder::class, 'folder1');
97
        $folder1Subfolder1 = $this->objFromFixture(Folder::class, 'folder1-subfolder1');
98
99
        $this->assertNotContains(
100
            $folder1->Name,
0 ignored issues
show
Bug Best Practice introduced by
The property Name does not exist on SilverStripe\ORM\DataObject. Since you implemented __get, consider adding a @property annotation.
Loading history...
101
            array_column($tree['children'], 'title'),
102
            $folder1->Name . ' is not found in the json'
103
        );
104
105
        $this->assertContains(
106
            $folder1Subfolder1->Name,
107
            array_column($tree['children'], 'title'),
108
            $folder1Subfolder1->Name . ' is found in the json'
109
        );
110
    }
111
112
    public function testTreeSearchJsonFlatlistWithLowNodeThreshold()
113
    {
114
        // Initialise our TreeDropDownField
115
        $field = new TreeDropdownField('TestTree', 'Test tree', TestObject::class);
116
        $field->config()->set('node_threshold_total', 2);
117
118
        // Search for all Test object matching our criteria
119
        $request = new HTTPRequest(
120
            'GET',
121
            'url',
122
            ['search' => 'MatchSearchCriteria', 'format' => 'json', 'flatList' => '1']
123
        );
124
        $request->setSession(new Session([]));
125
        $response = $field->tree($request);
126
        $tree = json_decode($response->getBody(), true);
127
        $actualNodeIDs = array_column($tree['children'], 'id');
128
129
130
        // Get the list of expected node IDs from the YML Fixture
131
        $expectedNodeIDs = array_map(
132
            function ($key) {
133
                return $this->objFromFixture(TestObject::class, $key)->ID;
134
            },
135
            ['zero', 'oneA', 'twoAi', 'three'] // Those are the identifiers of the object we expect our search to find
136
        );
137
138
        sort($actualNodeIDs);
139
        sort($expectedNodeIDs);
140
141
        $this->assertEquals($expectedNodeIDs, $actualNodeIDs);
142
    }
143
144
    public function testTreeSearchJsonFlatlistWithLowNodeThresholdUsingSubObject()
145
    {
146
        // Initialise our TreeDropDownField
147
        $field = new TreeDropdownField('TestTree', 'Test tree - Hierarchy on subclass', HierarchyOnSubclassTestSubObject::class);
148
        $field->config()->set('node_threshold_total', 2);
149
150
        // Search for all Test object matching our criteria
151
        $request = new HTTPRequest(
152
            'GET',
153
            'url',
154
            ['search' => 'SubObject', 'format' => 'json', 'flatList' => '1']
155
        );
156
        $request->setSession(new Session([]));
157
        $response = $field->tree($request);
158
        $tree = json_decode($response->getBody(), true);
159
        $actualNodeIDs = array_column($tree['children'], 'id');
160
161
        // Get the list of expected node IDs from the YML Fixture
162
        $expectedNodeIDs = array_map(
163
            function ($key) {
164
                return $this->objFromFixture(HierarchyOnSubclassTestSubObject::class, $key)->ID;
165
            },
166
            ['four', 'fourB', 'fourA2'] // Those are the identifiers of the object we expect our search to find
167
        );
168
169
        sort($actualNodeIDs);
170
        sort($expectedNodeIDs);
171
172
        $this->assertEquals($expectedNodeIDs, $actualNodeIDs);
173
    }
174
175
    public function testTreeSearch()
176
    {
177
        $field = new TreeDropdownField('TestTree', 'Test tree', Folder::class);
178
179
        // case-insensitive search against keyword 'sub' for folders
180
        $request = new HTTPRequest('GET', 'url', ['search'=>'sub']);
181
        $request->setSession(new Session([]));
182
        $response = $field->tree($request);
183
        $tree = $response->getBody();
184
185
        $folder1 = $this->objFromFixture(Folder::class, 'folder1');
186
        $folder1Subfolder1 = $this->objFromFixture(Folder::class, 'folder1-subfolder1');
187
188
        $parser = new CSSContentParser($tree);
189
        $cssPath = 'ul.tree li#selector-TestTree-' . $folder1->ID . ' li#selector-TestTree-' . $folder1Subfolder1->ID . ' a span.item';
190
        $firstResult = $parser->getBySelector($cssPath);
191
        $this->assertEquals(
192
            $folder1Subfolder1->Name,
0 ignored issues
show
Bug Best Practice introduced by
The property Name does not exist on SilverStripe\ORM\DataObject. Since you implemented __get, consider adding a @property annotation.
Loading history...
193
            (string)$firstResult[0],
194
            $folder1Subfolder1->Name . ' is found, nested under ' . $folder1->Name
195
        );
196
197
        $subfolder = $this->objFromFixture(Folder::class, 'subfolder');
198
        $cssPath = 'ul.tree li#selector-TestTree-' . $subfolder->ID . ' a span.item';
199
        $secondResult = $parser->getBySelector($cssPath);
200
        $this->assertEquals(
201
            $subfolder->Name,
202
            (string)$secondResult[0],
203
            $subfolder->Name . ' is found at root level'
204
        );
205
206
        // other folders which don't contain the keyword 'sub' are not returned in search results
207
        $folder2 = $this->objFromFixture(Folder::class, 'folder2');
208
        $cssPath = 'ul.tree li#selector-TestTree-' . $folder2->ID . ' a span.item';
209
        $noResult = $parser->getBySelector($cssPath);
210
        $this->assertEmpty(
211
            $noResult,
212
            $folder2 . ' is not found'
213
        );
214
215
        $field = new TreeDropdownField('TestTree', 'Test tree', File::class);
216
217
        // case-insensitive search against keyword 'sub' for files
218
        $request = new HTTPRequest('GET', 'url', ['search'=>'sub']);
219
        $request->setSession(new Session([]));
220
        $response = $field->tree($request);
221
        $tree = $response->getBody();
222
223
        $parser = new CSSContentParser($tree);
224
225
        // Even if we used File as the source object, folders are still returned because Folder is a File
226
        $cssPath = 'ul.tree li#selector-TestTree-' . $folder1->ID . ' li#selector-TestTree-' . $folder1Subfolder1->ID . ' a span.item';
227
        $firstResult = $parser->getBySelector($cssPath);
228
        $this->assertEquals(
229
            $folder1Subfolder1->Name,
230
            (string)$firstResult[0],
231
            $folder1Subfolder1->Name . ' is found, nested under ' . $folder1->Name
232
        );
233
234
        // Looking for two files with 'sub' in their name, both under the same folder
235
        $file1 = $this->objFromFixture(File::class, 'subfolderfile1');
236
        $file2 = $this->objFromFixture(File::class, 'subfolderfile2');
237
        $cssPath = 'ul.tree li#selector-TestTree-' . $subfolder->ID . ' li#selector-TestTree-' . $file1->ID . ' a';
238
        $firstResult = $parser->getBySelector($cssPath);
239
        $this->assertNotEmpty(
240
            $firstResult,
241
            $file1->Name . ' with ID ' . $file1->ID . ' is in search results'
242
        );
243
        $this->assertEquals(
244
            $file1->Name,
245
            (string)$firstResult[0],
246
            $file1->Name . ' is found nested under ' . $subfolder->Name
247
        );
248
249
        $cssPath = 'ul.tree li#selector-TestTree-' . $subfolder->ID . ' li#selector-TestTree-' . $file2->ID . ' a';
250
        $secondResult = $parser->getBySelector($cssPath);
251
        $this->assertNotEmpty(
252
            $secondResult,
253
            $file2->Name . ' with ID ' . $file2->ID . ' is in search results'
254
        );
255
        $this->assertEquals(
256
            $file2->Name,
257
            (string)$secondResult[0],
258
            $file2->Name . ' is found nested under ' . $subfolder->Name
259
        );
260
261
        // other files which don't include 'sub' are not returned in search results
262
        $file3 = $this->objFromFixture(File::class, 'asdf');
263
        $cssPath = 'ul.tree li#selector-TestTree-' . $file3->ID;
264
        $noResult = $parser->getBySelector($cssPath);
265
        $this->assertEmpty(
266
            $noResult,
267
            $file3->Name . ' is not found'
268
        );
269
    }
270
271
    public function testTreeSearchUsingSubObject()
272
    {
273
        $field = new TreeDropdownField('TestTree', 'Test tree', HierarchyOnSubclassTestSubObject::class);
274
275
        // case-insensitive search against keyword 'SubObject' for objects that have Hierarchy extension
276
        // applied to a class that doesn't directly inherit from DataObject
277
        $request = new HTTPRequest('GET', 'url', ['search' => 'SubObject']);
278
        $request->setSession(new Session([]));
279
        $response = $field->tree($request);
280
        $tree = $response->getBody();
281
282
        $subObject1 = $this->objFromFixture(HierarchyOnSubclassTestSubObject::class, 'four');
283
        $subObject1ChildB = $this->objFromFixture(HierarchyOnSubclassTestSubObject::class, 'fourB');
284
285
        $parser = new CSSContentParser($tree);
286
        $cssPath = 'ul.tree li#selector-TestTree-' . $subObject1->ID . ' li#selector-TestTree-' . $subObject1ChildB->ID . ' a';
287
        $firstResult = $parser->getBySelector($cssPath);
288
        $this->assertEquals(
289
            $subObject1ChildB->Name,
0 ignored issues
show
Bug Best Practice introduced by
The property Name does not exist on SilverStripe\ORM\DataObject. Since you implemented __get, consider adding a @property annotation.
Loading history...
290
            (string)$firstResult[0],
291
            $subObject1ChildB->Name . ' is found, nested under ' . $subObject1->Name
292
        );
293
294
        // other objects which don't contain the keyword 'SubObject' are not returned in search results
295
        $subObject2 = $this->objFromFixture(HierarchyOnSubclassTestSubObject::class, 'five');
296
        $cssPath = 'ul.tree li#selector-TestTree-' . $subObject2->ID . ' a';
297
        $noResult = $parser->getBySelector($cssPath);
298
        $this->assertEmpty(
299
            $noResult,
300
            $subObject2 . ' is not found'
301
        );
302
    }
303
304
    public function testReadonly()
305
    {
306
        $field = new TreeDropdownField('TestTree', 'Test tree', File::class);
307
        $fileMock = $this->objFromFixture(File::class, 'asdf');
308
        $field->setValue($fileMock->ID);
309
        $readonlyField = $field->performReadonlyTransformation();
310
        $result = (string) $readonlyField->Field();
311
        $this->assertContains(
312
            '<span class="readonly" id="TestTree">&lt;Special &amp; characters&gt;</span>',
313
            $result
314
        );
315
        $this->assertContains(
316
            '<input type="hidden" name="TestTree" value="' . $fileMock->ID . '" />',
317
            $result
318
        );
319
    }
320
321
    /**
322
     * This is to test setting $key to an Object in the protected function objectForKey()
323
     * This is to fix an issue where postgres will not fail gracefully when you do this
324
     */
325
    public function testObjectForKeyObjectValue()
326
    {
327
        $form = Form::create();
328
        $fieldList = FieldList::create();
329
        $field = TreeDropdownField::create('TestTree', 'Test tree', File::class);
330
        $fieldList->add($field);
331
        $form->setFields($fieldList);
332
        $field->setValue(DataObject::create());
333
        // The following previously errored in postgres
334
        $field->getSchemaStateDefaults();
335
        $this->assertTrue(true);
336
    }
337
338
    public function testTreeBaseID()
339
    {
340
        $treeBaseID = $this->idFromFixture(Folder::class, 'folder1');
341
        $field = new TreeDropdownField('TestTree', 'Test tree', Folder::class);
342
343
        // getSchemaDataDefaults needs the field to be attached to a form
344
        new Form(
345
            null,
346
            'mock',
347
            new FieldList($field)
348
        );
349
350
        $this->assertEmpty($field->getTreeBaseID(), 'TreeBaseId does not have an initial value');
351
352
        $field->setTreeBaseID($treeBaseID);
353
        $this->assertEquals(
354
            $treeBaseID,
355
            $field->getTreeBaseID(),
356
            'Value passed to setTreeBaseID is returned by getTreeBaseID'
357
        );
358
359
        $schema = $field->getSchemaDataDefaults();
360
        $this->assertEquals(
361
            $treeBaseID,
362
            $schema['data']['treeBaseId'],
363
            'TreeBaseId is included in the default schema data'
364
        );
365
    }
366
}
367