Passed
Push — master ( 2b7d8c...4b093d )
by Robbie
02:11
created

testLocalisedFieldsCanBeSorted()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 7
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 4
nc 1
nop 3
dl 0
loc 7
rs 10
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\State\FluentState;
11
use TractorCow\Fluent\Tests\Extension\FluentExtensionTest\LocalisedAnother;
12
use TractorCow\Fluent\Tests\Extension\FluentExtensionTest\LocalisedChild;
13
use TractorCow\Fluent\Tests\Extension\FluentExtensionTest\LocalisedParent;
14
use TractorCow\Fluent\Tests\Extension\FluentExtensionTest\UnlocalisedChild;
15
use TractorCow\Fluent\Tests\Extension\Stub\FluentStubObject;
16
17
class FluentExtensionTest extends SapphireTest
18
{
19
    protected static $fixture_file = 'FluentExtensionTest.yml';
20
21
    protected static $extra_dataobjects = [
22
        LocalisedAnother::class,
23
        LocalisedChild::class,
24
        LocalisedParent::class,
25
        UnlocalisedChild::class,
26
    ];
27
28
    protected static $required_extensions = [
29
        SiteTree::class => [
30
            FluentSiteTreeExtension::class,
31
        ],
32
    ];
33
34
    public function testFluentLocaleAndFrontendAreAddedToDataQuery()
35
    {
36
        FluentState::singleton()
37
            ->setLocale('test')
38
            ->setIsFrontend(true);
39
40
        $query = SiteTree::get()->dataQuery();
41
        $this->assertSame('test', $query->getQueryParam('Fluent.Locale'));
42
        $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

42
        $this->assertTrue(/** @scrutinizer ignore-type */ $query->getQueryParam('Fluent.IsFrontend'));
Loading history...
43
    }
44
45
    public function testGetLocalisedTable()
46
    {
47
        /** @var SiteTree|FluentSiteTreeExtension $page */
48
        $page = new SiteTree;
49
        $this->assertSame('SiteTree_Localised', $page->getLocalisedTable('SiteTree'));
50
        $this->assertSame(
51
            'SiteTree_Localised_FR',
52
            $page->getLocalisedTable('SiteTree', 'FR'),
53
            'Table aliases can be generated with getLocalisedTable()'
54
        );
55
    }
56
57
    public function testGetLinkingMode()
58
    {
59
        // Does not have a canViewInLocale method, locale is not current
60
        $stub = new FluentStubObject();
61
        $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

61
        $this->assertSame('link', $stub->/** @scrutinizer ignore-call */ getLinkingMode('foo'));
Loading history...
62
63
        // Does not have a canViewInLocale method, locale is current
64
        FluentState::singleton()->setLocale('foo');
65
        $this->assertSame('current', $stub->getLinkingMode('foo'));
66
    }
67
68
    public function testGetLocalisedFields()
69
    {
70
        // test data_include / data_exclude
71
        // note: These parent fields should be all accessible from the child records as well
72
        $parent = new LocalisedParent();
73
        $parentLocalised = [
74
            'Title' => 'Varchar',
75
            'Details' => 'Varchar(200)',
76
        ];
77
        $this->assertEquals(
78
            $parentLocalised,
79
            $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

79
            $parent->/** @scrutinizer ignore-call */ 
80
                     getLocalisedFields()
Loading history...
80
        );
81
82
        // test field_include / field_exclude
83
        $another = new LocalisedAnother();
84
        $this->assertEquals(
85
            [
86
                'Bastion' => 'Varchar',
87
                'Data' => 'Varchar(100)',
88
            ],
89
            $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

89
            $another->/** @scrutinizer ignore-call */ 
90
                      getLocalisedFields()
Loading history...
90
        );
91
        $this->assertEquals(
92
            $parentLocalised,
93
            $another->getLocalisedFields(LocalisedParent::class)
94
        );
95
96
        // Test translate directly
97
        $child = new LocalisedChild();
98
        $this->assertEquals(
99
            [ 'Record' => 'Text' ],
100
            $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

100
            $child->/** @scrutinizer ignore-call */ 
101
                    getLocalisedFields()
Loading history...
101
        );
102
        $this->assertEquals(
103
            $parentLocalised,
104
            $child->getLocalisedFields(LocalisedParent::class)
105
        );
106
107
        // Test 'none'
108
        $unlocalised = new UnlocalisedChild();
109
        $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

109
        $this->assertEmpty($unlocalised->/** @scrutinizer ignore-call */ getLocalisedFields());
Loading history...
110
        $this->assertEquals(
111
            $parentLocalised,
112
            $unlocalised->getLocalisedFields(LocalisedParent::class)
113
        );
114
    }
115
116
    public function testWritesToCurrentLocale()
117
    {
118
        FluentState::singleton()->setLocale('en_US');
119
        $record = $this->objFromFixture(LocalisedParent::class, 'record_a');
120
        $this->assertTrue(
121
            $this->hasLocalisedRecord($record, 'en_US'),
122
            'Record can be read from default locale'
123
        );
124
125
        FluentState::singleton()->setLocale('de_DE');
126
        $record2 = $this->objFromFixture(LocalisedParent::class, 'record_a');
127
        $this->assertTrue(
128
            $this->hasLocalisedRecord($record2, 'de_DE'),
129
            'Existing record can be read from German locale'
130
        );
131
132
        // There's no Spanish record yet, so this should create a record in the _Localised table
133
        FluentState::singleton()->setLocale('es_ES');
134
        $record2->Title = 'Un archivo';
135
        $record2->write();
136
137
        $record3 = $this->objFromFixture(LocalisedParent::class, 'record_a');
138
        $this->assertTrue(
139
            $this->hasLocalisedRecord($record3, 'es_ES'),
140
            'Record Locale is set to current locale when writing new records'
141
        );
142
    }
143
144
    /**
145
     * Get a Locale field value directly from a record's localised database table, skipping the ORM
146
     *
147
     * @param DataObject $record
148
     * @param string $locale
149
     * @return boolean
150
     */
151
    protected function hasLocalisedRecord(DataObject $record, $locale)
152
    {
153
        $result = SQLSelect::create()
154
            ->setFrom($record->config()->get('table_name') . '_Localised')
155
            ->setWhere([
156
                'RecordID' => $record->ID,
157
                'Locale' => $locale,
158
            ])
159
            ->execute()
160
            ->first();
161
162
        return !empty($result);
163
    }
164
165
    /**
166
     * Ensure that records can be sorted in their locales
167
     *
168
     * @dataProvider sortRecordProvider
169
     * @param string $locale
170
     * @param string[] $sortArgs
171
     * @param string[] $expected
172
     */
173
    public function testLocalisedFieldsCanBeSorted($locale, array $sortArgs, $expected)
174
    {
175
        FluentState::singleton()->setLocale($locale);
176
177
        $records = LocalisedParent::get()->sort(...$sortArgs);
178
        $titles = $records->column('Title');
179
        $this->assertEquals($expected, $titles);
180
    }
181
182
    /**
183
     * @return array[] Keys: Locale, sorting arguments, expected titles in result
184
     */
185
    public function sortRecordProvider()
186
    {
187
        return [
188
            /**
189
             * Single field (non-composite) sorting syntax (either string or array syntax)
190
             *
191
             * E.g. `->sort('"foo"')`, `->sort('Title', 'DESC')` etc
192
             */
193
            'german ascending single sort' => [
194
                'de_DE',
195
                ['Title', 'ASC'],
196
                ['Eine Akte', 'Lesen Sie mehr', 'Rennen'],
197
            ],
198
            'german descending single sort' => [
199
                'de_DE',
200
                ['"Title" DESC'],
201
                ['Rennen', 'Lesen Sie mehr', 'Eine Akte'],
202
            ],
203
            'english ascending single sort' => [
204
                'en_US',
205
                ['"Title" ASC'],
206
                ['A record', 'Go for a run', 'Read about things'],
207
            ],
208
            'english descending single sort' => [
209
                'en_US',
210
                ['Title', 'DESC'],
211
                ['Read about things', 'Go for a run', 'A record'],
212
            ],
213
            'english ascending on unlocalised field' => [
214
                'en_US',
215
                ['"Description"'],
216
                ['Read about things', 'Go for a run', 'A record'],
217
            ],
218
            'english descending on unlocalised field' => [
219
                'en_US',
220
                ['"Description" DESC'],
221
                ['A record', 'Read about things', 'Go for a run'],
222
            ],
223
            'german ascending on unlocalised field' => [
224
                'de_DE',
225
                ['"Description"'],
226
                ['Lesen Sie mehr', 'Rennen', 'Eine Akte'],
227
            ],
228
            'german descending on unlocalised field' => [
229
                'de_DE',
230
                ['"Description" DESC'],
231
                ['Eine Akte', 'Lesen Sie mehr', 'Rennen'],
232
            ],
233
            /**
234
             * Composite sorting tests (either string syntax or array syntax)
235
             *
236
             * E.g. `->sort(['foo' => 'ASC', 'bar' => 'DESC'])`
237
             */
238
            'english composite sort, string' => [
239
                'en_US',
240
                ['"Details" ASC, "Title" ASC'],
241
                ['Go for a run', 'A record', 'Read about things']
242
            ],
243
            'german composite sort, string' => [
244
                'de_DE',
245
                ['"Details" ASC, "Title" ASC'],
246
                ['Rennen', 'Eine Akte', 'Lesen Sie mehr'],
247
            ],
248
            'english, composite sort, array' => [
249
                'en_US',
250
                [[
251
                    'Details' => 'ASC',
252
                    'Title' => 'ASC'
253
                ]],
254
                ['Go for a run', 'A record', 'Read about things'],
255
            ],
256
            'german, composite sort, array' => [
257
                'de_DE',
258
                [[
259
                    'Details' => 'ASC',
260
                    'Title' => 'ASC'
261
                ]],
262
                ['Rennen', 'Eine Akte', 'Lesen Sie mehr'],
263
            ],
264
            'german, composite sort, array (flipped)' => [
265
                'de_DE',
266
                [[
267
                    'Details' => 'ASC',
268
                    'Title' => 'DESC'
269
                ]],
270
                ['Rennen', 'Lesen Sie mehr', 'Eine Akte'],
271
            ],
272
            'english, composite sort, array (flipped)' => [
273
                'en_US',
274
                [[
275
                    'Details' => 'DESC',
276
                    'Title' => 'DESC'
277
                ]],
278
                ['Read about things', 'A record', 'Go for a run'],
279
            ],
280
            'german, composite sort, no directions' => [
281
                'de_DE',
282
                ['"Details", "Title"'],
283
                ['Rennen', 'Eine Akte', 'Lesen Sie mehr'],
284
            ],
285
            /**
286
             * Ignored types of sorting, e.g. subqueries. Ignored sorting should use the ORM default
287
             * and sort on whatever is in the base table.
288
             */
289
            'english, subquery sort' => [
290
                'en_US',
291
                ['CONCAT((SELECT COUNT(*) FROM "FluentExtensionTest_LocalisedParent_Localised"), "FluentExtensionTest_LocalisedParent"."ID")'],
292
                ['A record', 'Read about things', 'Go for a run'],
293
            ]
294
        ];
295
    }
296
}
297