Passed
Push — master ( ba460f...5be854 )
by Robbie
01:56 queued 10s
created

FluentExtensionTest::testLocalisedMixSorting()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 29
Code Lines 15

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 15
nc 1
nop 0
dl 0
loc 29
rs 9.7666
c 0
b 0
f 0
1
<?php
2
3
namespace TractorCow\Fluent\Tests\Extension;
4
5
use SilverStripe\CMS\Model\SiteTree;
6
use SilverStripe\Dev\SapphireTest;
7
use SilverStripe\ORM\DataObject;
8
use SilverStripe\ORM\Queries\SQLSelect;
9
use TractorCow\Fluent\Extension\FluentSiteTreeExtension;
10
use TractorCow\Fluent\Model\Locale;
11
use TractorCow\Fluent\State\FluentState;
12
use TractorCow\Fluent\Tests\Extension\FluentExtensionTest\LocalisedAnother;
13
use TractorCow\Fluent\Tests\Extension\FluentExtensionTest\LocalisedChild;
14
use TractorCow\Fluent\Tests\Extension\FluentExtensionTest\LocalisedParent;
15
use TractorCow\Fluent\Tests\Extension\FluentExtensionTest\MixedLocalisedSortObject;
16
use TractorCow\Fluent\Tests\Extension\FluentExtensionTest\UnlocalisedChild;
17
use TractorCow\Fluent\Tests\Extension\Stub\FluentStubObject;
18
19
class FluentExtensionTest extends SapphireTest
20
{
21
    protected static $fixture_file = 'FluentExtensionTest.yml';
22
23
    protected static $extra_dataobjects = [
24
        LocalisedAnother::class,
25
        LocalisedChild::class,
26
        LocalisedParent::class,
27
        MixedLocalisedSortObject::class,
28
        UnlocalisedChild::class,
29
    ];
30
31
    protected static $required_extensions = [
32
        SiteTree::class => [
33
            FluentSiteTreeExtension::class,
34
        ],
35
    ];
36
37
    protected function setUp()
38
    {
39
        parent::setUp();
40
41
        Locale::clearCached();
42
    }
43
44
    public function testFluentLocaleAndFrontendAreAddedToDataQuery()
45
    {
46
        FluentState::singleton()->withState(function (FluentState $newState) {
47
            $newState
48
                ->setLocale('test')
49
                ->setIsFrontend(true);
50
51
            $query = SiteTree::get()->dataQuery();
52
            $this->assertSame('test', $query->getQueryParam('Fluent.Locale'));
53
            $this->assertTrue($query->getQueryParam('Fluent.IsFrontend'));
0 ignored issues
show
Bug introduced by
$query->getQueryParam('Fluent.IsFrontend') of type string is incompatible with the type boolean expected by parameter $condition of PHPUnit_Framework_Assert::assertTrue(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

53
            $this->assertTrue(/** @scrutinizer ignore-type */ $query->getQueryParam('Fluent.IsFrontend'));
Loading history...
54
        });
55
    }
56
57
    public function testGetLocalisedTable()
58
    {
59
        /** @var SiteTree|FluentSiteTreeExtension $page */
60
        $page = new SiteTree;
61
        $this->assertSame('SiteTree_Localised', $page->getLocalisedTable('SiteTree'));
62
        $this->assertSame(
63
            'SiteTree_Localised_FR',
64
            $page->getLocalisedTable('SiteTree', 'FR'),
65
            'Table aliases can be generated with getLocalisedTable()'
66
        );
67
    }
68
69
    public function testGetLinkingMode()
70
    {
71
        // Does not have a canViewInLocale method, locale is not current
72
        $stub = new FluentStubObject();
73
        $this->assertSame('link', $stub->getLinkingMode('foo'));
0 ignored issues
show
Bug introduced by
The method getLinkingMode() does not exist on TractorCow\Fluent\Tests\...n\Stub\FluentStubObject. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

73
        $this->assertSame('link', $stub->/** @scrutinizer ignore-call */ getLinkingMode('foo'));
Loading history...
74
75
        // Does not have a canViewInLocale method, locale is current
76
        FluentState::singleton()->withState(function (FluentState $newState) use ($stub) {
77
            $newState->setLocale('foo');
78
79
            $this->assertSame('current', $stub->getLinkingMode('foo'));
80
        });
81
    }
82
83
    public function testGetLocalisedFields()
84
    {
85
        // test data_include / data_exclude
86
        // note: These parent fields should be all accessible from the child records as well
87
        $parent = new LocalisedParent();
88
        $parentLocalised = [
89
            'Title' => 'Varchar',
90
            'Details' => 'Varchar(200)',
91
        ];
92
        $this->assertEquals(
93
            $parentLocalised,
94
            $parent->getLocalisedFields()
0 ignored issues
show
Bug introduced by
The method getLocalisedFields() does not exist on TractorCow\Fluent\Tests\...ionTest\LocalisedParent. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

94
            $parent->/** @scrutinizer ignore-call */ 
95
                     getLocalisedFields()
Loading history...
95
        );
96
97
        // test field_include / field_exclude
98
        $another = new LocalisedAnother();
99
        $this->assertEquals(
100
            [
101
                'Bastion' => 'Varchar',
102
                'Data' => 'Varchar(100)',
103
            ],
104
            $another->getLocalisedFields()
0 ignored issues
show
Bug introduced by
The method getLocalisedFields() does not exist on TractorCow\Fluent\Tests\...onTest\LocalisedAnother. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

104
            $another->/** @scrutinizer ignore-call */ 
105
                      getLocalisedFields()
Loading history...
105
        );
106
        $this->assertEquals(
107
            $parentLocalised,
108
            $another->getLocalisedFields(LocalisedParent::class)
109
        );
110
111
        // Test translate directly
112
        $child = new LocalisedChild();
113
        $this->assertEquals(
114
            [ 'Record' => 'Text' ],
115
            $child->getLocalisedFields()
0 ignored issues
show
Bug introduced by
The method getLocalisedFields() does not exist on TractorCow\Fluent\Tests\...sionTest\LocalisedChild. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

115
            $child->/** @scrutinizer ignore-call */ 
116
                    getLocalisedFields()
Loading history...
116
        );
117
        $this->assertEquals(
118
            $parentLocalised,
119
            $child->getLocalisedFields(LocalisedParent::class)
120
        );
121
122
        // Test 'none'
123
        $unlocalised = new UnlocalisedChild();
124
        $this->assertEmpty($unlocalised->getLocalisedFields());
0 ignored issues
show
Bug introduced by
The method getLocalisedFields() does not exist on TractorCow\Fluent\Tests\...onTest\UnlocalisedChild. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

124
        $this->assertEmpty($unlocalised->/** @scrutinizer ignore-call */ getLocalisedFields());
Loading history...
125
        $this->assertEquals(
126
            $parentLocalised,
127
            $unlocalised->getLocalisedFields(LocalisedParent::class)
128
        );
129
    }
130
131
    public function testWritesToCurrentLocale()
132
    {
133
        FluentState::singleton()->withState(function (FluentState $newState) {
134
            $newState->setLocale('en_US');
135
136
            $record = $this->objFromFixture(LocalisedParent::class, 'record_a');
137
            $this->assertTrue(
138
                $this->hasLocalisedRecord($record, 'en_US'),
139
                'Record can be read from default locale'
140
            );
141
        });
142
143
        FluentState::singleton()->withState(function (FluentState $newState) {
144
            $newState->setLocale('de_DE');
145
146
            $record2 = $this->objFromFixture(LocalisedParent::class, 'record_a');
147
            $this->assertTrue(
148
                $this->hasLocalisedRecord($record2, 'de_DE'),
149
                'Existing record can be read from German locale'
150
            );
151
152
            $newState->setLocale('es_ES');
153
154
            $record2->Title = 'Un archivo';
155
            $record2->write();
156
157
            $record3 = $this->objFromFixture(LocalisedParent::class, 'record_a');
158
            $this->assertTrue(
159
                $this->hasLocalisedRecord($record3, 'es_ES'),
160
                'Record Locale is set to current locale when writing new records'
161
            );
162
        });
163
    }
164
165
    public function testLocalisedMixSorting()
166
    {
167
        FluentState::singleton()->withState(function (FluentState $newState) {
168
            $newState->setLocale('en_US');
169
170
            // Sort by the NonLocalisedSort field first then the LocalisedField second both in ascending order
171
            // so the result will be opposite if the order of the columns is not maintained
172
            $objects=MixedLocalisedSortObject::get()->sort(
173
                '"FluentExtensionTest_MixedLocalisedSortObject"."LocalizedSort", '.
174
                '"FluentExtensionTest_MixedLocalisedSortObject"."NonLocalizedSort", '.
175
                '"FluentExtensionTest_MixedLocalisedSortObject"."Title"'
176
            );
177
178
            // Make sure Item A is first
179
            $this->assertEquals(
180
                'Item A',
181
                $objects->offsetGet(0)->Title
182
            );
183
184
            // Make sure Item B is second
185
            $this->assertEquals(
186
                'Item B',
187
                $objects->offsetGet(1)->Title
188
            );
189
190
            // Make sure Item C is third
191
            $this->assertEquals(
192
                'Item C',
193
                $objects->offsetGet(2)->Title
194
            );
195
        });
196
    }
197
198
    /**
199
     * Ensure that records can be sorted in their locales
200
     *
201
     * @dataProvider sortRecordProvider
202
     * @param string $locale
203
     * @param string[] $sortArgs
204
     * @param string[] $expected
205
     * @group exclude-from-travis
206
     */
207
    public function testLocalisedFieldsCanBeSorted($locale, array $sortArgs, $expected)
208
    {
209
        FluentState::singleton()->withState(function (FluentState $newState) use ($locale, $sortArgs, $expected) {
210
            $newState->setLocale($locale);
211
212
            $records = LocalisedParent::get()->sort(...$sortArgs);
213
            $titles = $records->column('Title');
214
            $this->assertEquals($expected, $titles);
215
        });
216
    }
217
218
    /**
219
     * @return array[] Keys: Locale, sorting arguments, expected titles in result
220
     */
221
    public function sortRecordProvider()
222
    {
223
        return [
224
            /**
225
             * Single field (non-composite) sorting syntax (either string or array syntax)
226
             *
227
             * E.g. `->sort('"foo"')`, `->sort('Title', 'DESC')` etc
228
             */
229
            'german ascending single sort' => [
230
                'de_DE',
231
                ['Title', 'ASC'],
232
                ['Eine Akte', 'Lesen Sie mehr', 'Rennen'],
233
            ],
234
            'german descending single sort' => [
235
                'de_DE',
236
                ['"Title" DESC'],
237
                ['Rennen', 'Lesen Sie mehr', 'Eine Akte'],
238
            ],
239
            'english ascending single sort' => [
240
                'en_US',
241
                ['"Title" ASC'],
242
                ['A record', 'Go for a run', 'Read about things'],
243
            ],
244
            'english descending single sort' => [
245
                'en_US',
246
                ['Title', 'DESC'],
247
                ['Read about things', 'Go for a run', 'A record'],
248
            ],
249
            'english ascending on unlocalised field' => [
250
                'en_US',
251
                ['"Description"'],
252
                ['Read about things', 'Go for a run', 'A record'],
253
            ],
254
            'english descending on unlocalised field' => [
255
                'en_US',
256
                ['"Description" DESC'],
257
                ['A record', 'Read about things', 'Go for a run'],
258
            ],
259
            'german ascending on unlocalised field' => [
260
                'de_DE',
261
                ['"Description"'],
262
                ['Lesen Sie mehr', 'Rennen', 'Eine Akte'],
263
            ],
264
            'german descending on unlocalised field' => [
265
                'de_DE',
266
                ['"Description" DESC'],
267
                ['Eine Akte', 'Lesen Sie mehr', 'Rennen'],
268
            ],
269
            /**
270
             * Composite sorting tests (either string syntax or array syntax)
271
             *
272
             * E.g. `->sort(['foo' => 'ASC', 'bar' => 'DESC'])`
273
             */
274
            'english composite sort, string' => [
275
                'en_US',
276
                ['"Details" ASC, "Title" ASC'],
277
                ['Go for a run', 'A record', 'Read about things']
278
            ],
279
            'german composite sort, string' => [
280
                'de_DE',
281
                ['"Details" ASC, "Title" ASC'],
282
                ['Rennen', 'Eine Akte', 'Lesen Sie mehr'],
283
            ],
284
            'english, composite sort, array' => [
285
                'en_US',
286
                [[
287
                    'Details' => 'ASC',
288
                    'Title' => 'ASC'
289
                ]],
290
                ['Go for a run', 'A record', 'Read about things'],
291
            ],
292
            'german, composite sort, array' => [
293
                'de_DE',
294
                [[
295
                    'Details' => 'ASC',
296
                    'Title' => 'ASC'
297
                ]],
298
                ['Rennen', 'Eine Akte', 'Lesen Sie mehr'],
299
            ],
300
            'german, composite sort, array (flipped)' => [
301
                'de_DE',
302
                [[
303
                    'Details' => 'ASC',
304
                    'Title' => 'DESC'
305
                ]],
306
                ['Rennen', 'Lesen Sie mehr', 'Eine Akte'],
307
            ],
308
            'english, composite sort, array (flipped)' => [
309
                'en_US',
310
                [[
311
                    'Details' => 'DESC',
312
                    'Title' => 'DESC'
313
                ]],
314
                ['Read about things', 'A record', 'Go for a run'],
315
            ],
316
            'german, composite sort, no directions' => [
317
                'de_DE',
318
                ['"Details", "Title"'],
319
                ['Rennen', 'Eine Akte', 'Lesen Sie mehr'],
320
            ],
321
            /**
322
             * Ignored types of sorting, e.g. subqueries. Ignored sorting should use the ORM default
323
             * and sort on whatever is in the base table.
324
             */
325
            'english, subquery sort' => [
326
                'en_US',
327
                ['CONCAT((SELECT COUNT(*) FROM "FluentExtensionTest_LocalisedParent_Localised"), "FluentExtensionTest_LocalisedParent"."ID")'],
328
                ['A record', 'Read about things', 'Go for a run'],
329
            ]
330
        ];
331
    }
332
333
    /**
334
     * Get a Locale field value directly from a record's localised database table, skipping the ORM
335
     *
336
     * @param DataObject $record
337
     * @param string $locale
338
     * @return boolean
339
     */
340
    protected function hasLocalisedRecord(DataObject $record, $locale)
341
    {
342
        $result = SQLSelect::create()
343
            ->setFrom($record->config()->get('table_name') . '_Localised')
344
            ->setWhere([
345
                'RecordID' => $record->ID,
346
                'Locale' => $locale,
347
            ])
348
            ->execute()
349
            ->first();
350
351
        return !empty($result);
352
    }
353
}
354