Issues (37)

tests/php/Extension/FluentExtensionTest.php (6 issues)

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()->withState(function (FluentState $newState) {
37
            $newState
38
                ->setLocale('test')
39
                ->setIsFrontend(true);
40
41
            $query = SiteTree::get()->dataQuery();
42
            $this->assertSame('test', $query->getQueryParam('Fluent.Locale'));
43
            $this->assertTrue($query->getQueryParam('Fluent.IsFrontend'));
0 ignored issues
show
$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

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

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

84
            $parent->/** @scrutinizer ignore-call */ 
85
                     getLocalisedFields()
Loading history...
85
        );
86
87
        // test field_include / field_exclude
88
        $another = new LocalisedAnother();
89
        $this->assertEquals(
90
            [
91
                'Bastion' => 'Varchar',
92
                'Data' => 'Varchar(100)',
93
            ],
94
            $another->getLocalisedFields()
0 ignored issues
show
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

94
            $another->/** @scrutinizer ignore-call */ 
95
                      getLocalisedFields()
Loading history...
95
        );
96
        $this->assertEquals(
97
            $parentLocalised,
98
            $another->getLocalisedFields(LocalisedParent::class)
99
        );
100
101
        // Test translate directly
102
        $child = new LocalisedChild();
103
        $this->assertEquals(
104
            [ 'Record' => 'Text' ],
105
            $child->getLocalisedFields()
0 ignored issues
show
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

105
            $child->/** @scrutinizer ignore-call */ 
106
                    getLocalisedFields()
Loading history...
106
        );
107
        $this->assertEquals(
108
            $parentLocalised,
109
            $child->getLocalisedFields(LocalisedParent::class)
110
        );
111
112
        // Test 'none'
113
        $unlocalised = new UnlocalisedChild();
114
        $this->assertEmpty($unlocalised->getLocalisedFields());
0 ignored issues
show
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

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