ILIAS  trunk Revision v13.0_alpha-62-g3b72d83f2e5
SetLanguageTranslationEnabledTest.php
Go to the documentation of this file.
1<?php
2
14declare(strict_types=1);
15
17
24use ILIAS\Data\Description\Factory as DescriptionFactory;
25use ILIAS\UI\Component\Input\Factory as InputFactory;
26use PHPUnit\Framework\Attributes\DataProvider;
27use PHPUnit\Framework\MockObject\MockObject;
28
35{
37 {
38 return $this->createActivity();
39 }
40
42 {
43 $rbac = $this->createMock(\ilRbacSystem::class);
44 $rbac->expects($this->once())
45 ->method('checkAccessOfUser')
46 ->with(6, 'write', 42)
47 ->willReturn(true);
48
49 $activity = $this->createActivity(rbac_system: $rbac, language_folder_ref_id: 42);
50
51 $this->assertTrue($activity->isAllowedToPerform(6, ['language_key' => 'de', 'enabled' => true]));
52 }
53
55 {
56 $rbac = $this->createStub(\ilRbacSystem::class);
57 $rbac->method('checkAccessOfUser')->willReturn(false);
58
59 $activity = $this->createActivity(rbac_system: $rbac);
60
61 $this->assertFalse($activity->isAllowedToPerform(6, ['language_key' => 'de', 'enabled' => true]));
62 }
63
66 {
67 $repository = $this->createMock(RepositoryProvider::class);
68 $repository->expects($this->never())->method('isPageTranslationEnabled');
69 $repository->expects($this->never())->method('setPageTranslationEnabled');
70
71 $rbac = $this->createStub(\ilRbacSystem::class);
72 $rbac->method('checkAccessOfUser')->willReturn(true);
73
74 $activity = $this->createActivity(rbac_system: $rbac, repository: $repository);
75
76 $this->assertTrue($activity->isAllowedToPerform(6, ['language_key' => 'de', 'enabled' => true]));
77 }
78
80 public static function performChangedDecisionProvider(): array
81 {
82 return [
83 'stored off, request on -> changed, write on' => [false, true, true, true],
84 'stored off, request off -> unchanged, no write' => [false, false, false, null],
85 'stored on, request on -> unchanged, no write' => [true, true, false, null],
86 'stored on, request off -> changed, write off' => [true, false, true, false],
87 ];
88 }
89
90 #[DataProvider('performChangedDecisionProvider')]
92 bool $stored_value,
93 bool $requested_enabled,
94 bool $expected_changed,
95 ?bool $expected_written_value
96 ): void {
97 $repository = $this->createMock(RepositoryProvider::class);
98 $repository->expects($this->once())->method('isPageTranslationEnabled')
99 ->with('de')
100 ->willReturn($stored_value);
101
102 if ($expected_written_value === null) {
103 $repository->expects($this->never())->method('setPageTranslationEnabled');
104 } else {
105 $repository->expects($this->once())
106 ->method('setPageTranslationEnabled')
107 ->with('de', $expected_written_value);
108 }
109
110 $result = $this->createActivity(repository: $repository)
111 ->perform(['language_key' => 'de', 'enabled' => $requested_enabled]);
112
113 $this->assertSame(
114 ['language_key' => 'de', 'enabled' => $requested_enabled, 'changed' => $expected_changed],
115 $result
116 );
117 }
118
121 {
122 $repository = $this->createMock(RepositoryProvider::class);
123 $repository->expects($this->once())
124 ->method('isPageTranslationEnabled')->with('fr')->willReturn(false);
125
126 $this->createActivity(repository: $repository)->perform(['language_key' => 'fr', 'enabled' => false]);
127 }
128
131 {
132 $repository = $this->createMock(RepositoryProvider::class);
133 $repository->expects($this->once())->method('isPageTranslationEnabled')->with('de')->willReturn(false);
134 $repository->expects($this->once())->method('setPageTranslationEnabled')->with('de', true);
135
136 $result = $this->createActivity(repository: $repository)
137 ->perform(['language_key' => ' de ', 'enabled' => true]);
138
139 $this->assertSame('de', $result['language_key']);
140 }
141
142 public static function invalidPerformParametersProvider(): array
143 {
144 return [
145 'non-array parameters' => ['not-an-array'],
146 'empty array' => [[]],
147 'missing language_key' => [['enabled' => true]],
148 'non-string language_key' => [['language_key' => 42, 'enabled' => true]],
149 'empty string language_key' => [['language_key' => '', 'enabled' => true]],
150 'whitespace-only language_key' => [['language_key' => ' ', 'enabled' => true]],
151 'missing enabled' => [['language_key' => 'de']],
152 'non-bool enabled: string "1"' => [['language_key' => 'de', 'enabled' => '1']],
153 'non-bool enabled: string "true"' => [['language_key' => 'de', 'enabled' => 'true']],
154 'non-bool enabled: string ""' => [['language_key' => 'de', 'enabled' => '']],
155 'non-bool enabled: int 1' => [['language_key' => 'de', 'enabled' => 1]],
156 'non-bool enabled: int 0' => [['language_key' => 'de', 'enabled' => 0]],
157 'non-bool enabled: float 1.0' => [['language_key' => 'de', 'enabled' => 1.0]],
158 'non-bool enabled: null' => [['language_key' => 'de', 'enabled' => null]],
159 'non-bool enabled: array' => [['language_key' => 'de', 'enabled' => [true]]],
160 ];
161 }
162
163 #[DataProvider('invalidPerformParametersProvider')]
165 {
166 $repository = $this->createMock(RepositoryProvider::class);
167 $repository->expects($this->never())->method('isPageTranslationEnabled');
168 $repository->expects($this->never())->method('setPageTranslationEnabled');
169
170 $this->expectException(InvalidInputException::class);
171
172 $this->createActivity(repository: $repository)->perform($parameters);
173 }
174
176 {
177 $rbac = $this->createStub(\ilRbacSystem::class);
178 $rbac->method('checkAccessOfUser')->willReturn(true);
179
180 $repository = $this->createMock(RepositoryProvider::class);
181 $repository->expects($this->once())->method('isPageTranslationEnabled')->with('de')->willReturn(false);
182 $repository->expects($this->once())->method('setPageTranslationEnabled')->with('de', true);
183
184 $result = $this->createActivity(rbac_system: $rbac, repository: $repository)
185 ->maybePerformAs($this->createRealFieldsUiFactory()->input(), 6, ['language_key' => 'de', 'enabled' => true]);
186
187 $this->assertFalse($result->isError());
188 $this->assertSame(
189 ['language_key' => 'de', 'enabled' => true, 'changed' => true],
190 $result->value()
191 );
192 }
193
196 {
197 $rbac = $this->createMock(\ilRbacSystem::class);
198 $rbac->expects($this->once())
199 ->method('checkAccessOfUser')
200 ->with(6, 'write', $this->anything())
201 ->willReturn(false);
202
203 $repository = $this->createMock(RepositoryProvider::class);
204 $repository->expects($this->never())->method('isPageTranslationEnabled');
205 $repository->expects($this->never())->method('setPageTranslationEnabled');
206
207 $result = $this->createActivity(rbac_system: $rbac, repository: $repository)
208 ->maybePerformAs($this->createRealFieldsUiFactory()->input(), 6, ['language_key' => 'de', 'enabled' => true]);
209
210 $this->assertTrue($result->isError());
211 $this->assertInstanceOf(\DomainException::class, $result->error());
212 $this->assertSame(403, $result->error()->getCode());
213 }
214
217 {
218 $rbac = $this->createStub(\ilRbacSystem::class);
219 $rbac->method('checkAccessOfUser')->willReturn(true);
220
221 $repository = $this->createMock(RepositoryProvider::class);
222 $repository->method('isPageTranslationEnabled')->willReturn(false);
223 $repository->method('setPageTranslationEnabled')->willThrowException(new \RuntimeException('database write failed'));
224
225 $result = $this->createActivity(rbac_system: $rbac, repository: $repository)
226 ->maybePerformAs($this->createRealFieldsUiFactory()->input(), 6, ['language_key' => 'de', 'enabled' => true]);
227
228 $this->assertTrue($result->isError());
229 $error = $result->error();
230 $this->assertSame(500, $error->getCode());
231 $this->assertSame('database write failed', $error->getPrevious()?->getMessage());
232 }
233
236 {
237 $rbac = $this->createMock(\ilRbacSystem::class);
238 $rbac->expects($this->never())->method('checkAccessOfUser');
239
240 $result = $this->createActivity(rbac_system: $rbac)->maybePerformAs($this->createRealFieldsUiFactory()->input(), 6, []);
241
242 $this->assertTrue($result->isError());
243 $this->assertInstanceOf(InvalidInputException::class, $result->error());
244 $this->assertNotSame('', $result->error()->getMessage());
245 }
246
248 {
249 return [
250 'missing language_key' => [['enabled' => true]],
251 ];
252 }
253
254 #[DataProvider('invalidRawParametersRejectedByGrindWithStringErrorProvider')]
256 array $raw_parameters
257 ): void {
258 $rbac = $this->createMock(\ilRbacSystem::class);
259 $rbac->expects($this->never())->method('checkAccessOfUser');
260
261 $repository = $this->createMock(RepositoryProvider::class);
262 $repository->expects($this->never())->method('isPageTranslationEnabled');
263 $repository->expects($this->never())->method('setPageTranslationEnabled');
264
265 $result = $this->createActivity(rbac_system: $rbac, repository: $repository)
266 ->maybePerformAs($this->createRealFieldsUiFactory()->input(), 6, $raw_parameters);
267
268 $this->assertTrue($result->isError());
269 $this->assertInstanceOf(InvalidInputException::class, $result->error());
270 $this->assertNotSame('', $result->error()->getMessage());
271 }
272
273 public static function invalidRawParametersProvider(): array
274 {
275 return [
276 // a non-string 'language_key' fails the Text field's isClientSideValueOk() inside grind()
277 'non-string language_key' => [['language_key' => 42, 'enabled' => true]],
278 // outside the whitelist of GrindsFormInput::normalizeCheckboxRawValue()
279 'non-bool enabled: array' => [['language_key' => 'de', 'enabled' => [true]]],
280 'non-bool enabled: float 1.0' => [['language_key' => 'de', 'enabled' => 1.0]],
281 ];
282 }
283
284 #[DataProvider('invalidRawParametersProvider')]
286 array $raw_parameters
287 ): void {
288 $rbac = $this->createMock(\ilRbacSystem::class);
289 $rbac->expects($this->never())->method('checkAccessOfUser');
290
291 $repository = $this->createMock(RepositoryProvider::class);
292 $repository->expects($this->never())->method('isPageTranslationEnabled');
293 $repository->expects($this->never())->method('setPageTranslationEnabled');
294
295 $result = $this->createActivity(rbac_system: $rbac, repository: $repository)
296 ->maybePerformAs($this->createRealFieldsUiFactory()->input(), 6, $raw_parameters);
297
298 $this->assertTrue($result->isError());
299 $this->assertInstanceOf(InvalidInputException::class, $result->error());
300 $this->assertSame(400, $result->error()->getCode());
301 }
302
305 {
306 $rbac = $this->createMock(\ilRbacSystem::class);
307 $rbac->expects($this->once())->method('checkAccessOfUser')->willReturn(true);
308
309 $repository = $this->createMock(RepositoryProvider::class);
310 $repository->expects($this->once())->method('isPageTranslationEnabled')->with('de')->willReturn(true);
311 $repository->expects($this->once())->method('setPageTranslationEnabled')->with('de', false);
312
313 $result = $this->createActivity(rbac_system: $rbac, repository: $repository)
314 ->maybePerformAs($this->createRealFieldsUiFactory()->input(), 6, ['language_key' => 'de']);
315
316 $this->assertFalse($result->isError());
317 $this->assertSame(
318 ['language_key' => 'de', 'enabled' => false, 'changed' => true],
319 $result->value()
320 );
321 }
322
325 {
326 $rbac = $this->createMock(\ilRbacSystem::class);
327 $rbac->expects($this->once())->method('checkAccessOfUser')->willReturn(true);
328
329 $repository = $this->createMock(RepositoryProvider::class);
330 $repository->expects($this->never())->method('isPageTranslationEnabled');
331 $repository->expects($this->never())->method('setPageTranslationEnabled');
332
333 $result = $this->createActivity(rbac_system: $rbac, repository: $repository)
334 ->maybePerformAs($this->createRealFieldsUiFactory()->input(), 6, ['language_key' => ' ', 'enabled' => true]);
335
336 $this->assertTrue($result->isError());
337 $this->assertInstanceOf(InvalidInputException::class, $result->error());
338 $this->assertSame(400, $result->error()->getCode());
339 }
340
341 public static function tolerantlyAcceptedTruthyEnabledValuesProvider(): array
342 {
343 return [
344 'string "1"' => ['1'],
345 'string "true"' => ['true'],
346 'int 1' => [1],
347 ];
348 }
349
350 public static function tolerantlyAcceptedFalsyEnabledValuesProvider(): array
351 {
352 return [
353 'string "0"' => ['0'],
354 'string "false"' => ['false'],
355 'string ""' => [''],
356 'int 0' => [0],
357 'null' => [null],
358 ];
359 }
360
362 #[DataProvider('tolerantlyAcceptedTruthyEnabledValuesProvider')]
364 {
365 $rbac = $this->createStub(\ilRbacSystem::class);
366 $rbac->method('checkAccessOfUser')->willReturn(true);
367
368 $repository = $this->createMock(RepositoryProvider::class);
369 $repository->expects($this->once())->method('isPageTranslationEnabled')->with('de')->willReturn(false);
370 $repository->expects($this->once())->method('setPageTranslationEnabled')->with('de', true);
371
372 $result = $this->createActivity(rbac_system: $rbac, repository: $repository)
373 ->maybePerformAs($this->createRealFieldsUiFactory()->input(), 6, ['language_key' => 'de', 'enabled' => $value]);
374
375 $this->assertFalse($result->isError());
376 $this->assertSame(
377 ['language_key' => 'de', 'enabled' => true, 'changed' => true],
378 $result->value()
379 );
380 }
381
383 #[DataProvider('tolerantlyAcceptedFalsyEnabledValuesProvider')]
385 {
386 $rbac = $this->createStub(\ilRbacSystem::class);
387 $rbac->method('checkAccessOfUser')->willReturn(true);
388
389 $repository = $this->createMock(RepositoryProvider::class);
390 $repository->expects($this->once())->method('isPageTranslationEnabled')->with('de')->willReturn(true);
391 $repository->expects($this->once())->method('setPageTranslationEnabled')->with('de', false);
392
393 $result = $this->createActivity(rbac_system: $rbac, repository: $repository)
394 ->maybePerformAs($this->createRealFieldsUiFactory()->input(), 6, ['language_key' => 'de', 'enabled' => $value]);
395
396 $this->assertFalse($result->isError());
397 $this->assertSame(
398 ['language_key' => 'de', 'enabled' => false, 'changed' => true],
399 $result->value()
400 );
401 }
402
405 {
406 $repository = $this->createMock(RepositoryProvider::class);
407 $repository->expects($this->never())->method('isPageTranslationEnabled');
408 $repository->expects($this->never())->method('setPageTranslationEnabled');
409
410 $this->expectException(InvalidInputException::class);
411
412 $this->createActivity(repository: $repository)
413 ->perform(['language_key' => 'xx', 'enabled' => true]);
414 }
415
418 {
419 $repository = $this->createMock(RepositoryProvider::class);
420 $repository->expects($this->never())->method('isPageTranslationEnabled');
421 $repository->expects($this->never())->method('setPageTranslationEnabled');
422
423 $dangerous_key = '<script>alert(1)</script>&"quoted"';
424
425 try {
426 $this->createActivity(repository: $repository)
427 ->perform(['language_key' => $dangerous_key, 'enabled' => true]);
428 $this->fail('Expected an InvalidInputException to be thrown.');
429 } catch (InvalidInputException $e) {
430 $this->assertStringContainsString($dangerous_key, $e->getMessage());
431 $this->assertStringNotContainsString('&lt;script&gt;', $e->getMessage());
432 $this->assertStringNotContainsString('&quot;', $e->getMessage());
433 }
434 }
435
438 {
439 $rbac = $this->createMock(\ilRbacSystem::class);
440 $rbac->expects($this->once())->method('checkAccessOfUser')->willReturn(true);
441
442 $repository = $this->createMock(RepositoryProvider::class);
443 $repository->expects($this->never())->method('isPageTranslationEnabled');
444 $repository->expects($this->never())->method('setPageTranslationEnabled');
445
446 $result = $this->createActivity(rbac_system: $rbac, repository: $repository)
447 ->maybePerformAs($this->createRealFieldsUiFactory()->input(), 6, ['language_key' => 'xx', 'enabled' => true]);
448
449 $this->assertTrue($result->isError());
450 $this->assertInstanceOf(InvalidInputException::class, $result->error());
451 $this->assertSame(400, $result->error()->getCode());
452 }
453
456 {
457 $rbac = $this->createStub(\ilRbacSystem::class);
458 $rbac->method('checkAccessOfUser')->willReturn(true);
459
460 $repository = $this->createMock(RepositoryProvider::class);
461 $repository->expects($this->once())->method('isPageTranslationEnabled')->with('de')->willReturn(false);
462 $repository->expects($this->once())->method('setPageTranslationEnabled')->with('de', true);
463
464 $result = $this->createActivity(rbac_system: $rbac, repository: $repository)
465 ->maybePerformAs($this->createRealFieldsUiFactory()->input(), 6, ['language_key' => ' de ', 'enabled' => true]);
466
467 $this->assertFalse($result->isError());
468 $this->assertSame('de', $result->value()['language_key']);
469 }
470
472 {
473 $text = $this->createMock(Text::class);
474 $text->expects($this->once())->method('withRequired')->with(true)->willReturnSelf();
475 $text->expects($this->once())->method('withDedicatedName')->with('language_key')->willReturnSelf();
476
477 $checkbox = $this->createMock(Checkbox::class);
478 $checkbox->expects($this->once())
479 ->method('withDedicatedName')
480 ->with('enabled')
481 ->willReturnSelf();
482
483 $field = $this->createMock(\ILIAS\UI\Component\Input\Field\Factory::class);
484 $field->expects($this->once())
485 ->method('text')
486 ->with(
487 'Language key',
488 'Language key the page translation setting applies to, e.g. de, fr, it.'
489 )
490 ->willReturn($text);
491 $field->expects($this->once())
492 ->method('checkbox')
493 ->with(
494 'Enabled',
495 'Whether page translation should be enabled for this language.'
496 )
497 ->willReturn($checkbox);
498
499 $group = $this->createMock(\ILIAS\UI\Component\Input\Field\Section::class);
500 $field->expects($this->once())
501 ->method('section')
502 ->with(['language_key' => $text, 'enabled' => $checkbox], $this->anything())
503 ->willReturn($group);
504
505 $activity = $this->createActivity();
506
507 $this->assertSame($group, $activity->getInputDescription($field));
508 }
509
512 {
513 $activity = $this->createActivity();
514
515 $description = $activity->getOutputDescription(new DescriptionFactory());
516
517 $field_names = [];
518 foreach ($description->getFields() as $field) {
519 $field_names[] = $field->getName();
520 }
521
522 $this->assertSame(['language_key', 'enabled', 'changed'], $field_names);
523 }
524
525 private function createActivity(
526 ?\ilRbacSystem $rbac_system = null,
527 (RepositoryProvider&MockObject)|null $repository = null,
528 int $language_folder_ref_id = 0,
529 array $installed_languages = ['de', 'fr'],
531 $repository ??= $this->createMock(RepositoryProvider::class);
532 $repository->method('getInstalledLanguages')->willReturn($installed_languages);
533
535 $this->rbacOf($rbac_system ?? $this->createMock(\ilRbacSystem::class)),
536 $this->languageFolder($language_folder_ref_id),
537 $repository,
538 );
539 }
540}
Every legacy and repository access of the runtime language activities.
Contract tests shared by every Activity test case; subclasses only supply createDefaultActivity().
Unlike SetLanguageDetectionEnabled it is parameterised by a language_key, and perform() itself valida...
testMaybePerformAsToleratesCommonPrimitiveTruthyEnabledRepresentations(mixed $value)
Unlike perform(), maybePerformAs() accepts common primitive representations of "true".
testMaybePerformAsTrimsLanguageKeyBeforeBuildingTheSettingKey()
A language_key with surrounding whitespace resolves to the same setting key as via perform().
testPerformWritesOnlyWhenTheRequestedValueDiffersFromTheStoredOne(bool $stored_value, bool $requested_enabled, bool $expected_changed, ?bool $expected_written_value)
testMaybePerformAsRejectsWhitespaceOnlyLanguageKeyAfterThePermissionCheck()
A whitespace-only language_key passes grind()'s required-check, so perform() rejects it after the per...
testMaybePerformAsWithMissingRawParametersKeysIsAResultErrorAndNeverChecksPermission()
A missing 'language_key' fails the required Text field in grind(), before rbac is touched.
createActivity(?\ilRbacSystem $rbac_system=null,(RepositoryProvider &MockObject)|null $repository=null, int $language_folder_ref_id=0, array $installed_languages=['de', 'fr'],)
testMaybePerformAsWithMissingEnabledKeyDefaultsToFalseAndStillPerformsTheWrite()
A missing 'enabled' defaults to false like an unchecked HTML checkbox, and still reaches perform().
testPerformReadsTheCorrectPerLanguageSettingKeyWithDefaultZero()
Pins the key "lang_translate_<language_key>" and the default '0'.
testPerformRejectsUnknownLanguageKeyWithoutTouchingSettings()
An unknown language_key is rejected by perform() itself, for direct callers too.
testPerformRejectsUnknownLanguageKeyMessageContainsTheRawUnescapedKey()
The message embeds the key raw: escaping is RendersActivityErrors' job.
testIsAllowedToPerformNeverTouchesSettings()
isAllowedToPerform() is a pure permission check: no Setting access.
testPerformTrimsLanguageKeyForBothTheSettingKeyAndTheReturnedValue()
perform() re-implements its own trim() of language_key.
testOutputDescriptionDescribesTheThreeDocumentedFieldsInOrder()
Real DescriptionFactory: a mocked with([...]) would accept the fields in any order.
static performChangedDecisionProvider()
Only a difference between the stored and the requested value is written.
testDefectFromWithinPerformIsMaskedAsA500ResultError()
A Throwable from perform() becomes a Result\Error.
testMaybePerformAsWithDeniedPermissionReturnsErrorAndNeverTouchesSettings()
A denied "write" yields a Result\Error and leaves the Setting untouched.
testMaybePerformAsRejectsUnknownLanguageKeyAfterPermissionCheckAndNeverTouchesSettings()
Same rejection end-to-end: it passes grind() and the permission check, perform() rejects it.
Definition: UI.php:27
ilErrorHandling $error
Definition: class.ilias.php:69
class ilRbacSystem system function like checkAccess, addActiveRole ... Supporting system functions ar...
This is how a factory for inputs looks like.
Definition: Factory.php:27
This describes checkbox inputs.
Definition: Checkbox.php:29
This describes text inputs.
Definition: Text.php:29
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
createActivity(\Closure $lng_objects, \Closure $obj_language_factory, ?\ilRbacSystem $rbac=null, int $language_folder_ref_id=0, ?RepositoryProvider $repository=null,)
Interface Observer \BackgroundTasks Contains several chained tasks and infos about them.
$text
Definition: xapiexit.php:21