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
Bug
introduced
by
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
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
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
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
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
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 |