Fassung 1.7.0: verlässliche Installation, übersichtliche Verwaltung und bedienbare Bewegung
This commit is contained in:
1 parent
b164c4edfe
commit
495b26896e
55 files changed
+1632
-423
No files matched your search
+63
-36
@@ -85,6 +85,43 @@ final class CmsTest extends TestCase
|
||||
}
|
||||
}
|
||||
|
||||
public function testEditorPaginationAndChoicesKeepTypesFiltersAndParentLookup(): void
|
||||
{
|
||||
$ids = [];
|
||||
for ($i = 0; $i < 43; ++$i) {
|
||||
$document = DemoContent::pages()['home'];
|
||||
$document['title'] = 'Eintrag '.sprintf('%02d', $i);
|
||||
$ids[] = $this->pages->createPage('eintrag-'.sprintf('%02d', $i), null, 'post', $document);
|
||||
}
|
||||
$first = $this->pages->editorPage('post');
|
||||
$second = $this->pages->editorPage('post', 2);
|
||||
self::assertSame(43, $first['total']);
|
||||
self::assertCount(20, $first['items']);
|
||||
self::assertCount(20, $second['items']);
|
||||
self::assertSame([], array_intersect(array_column($first['items'], 'id'), array_column($second['items'], 'id')));
|
||||
self::assertCount(3, $this->pages->editorPage('post', 999)['items']);
|
||||
self::assertSame(3, $this->pages->editorPage('post', 999)['page']);
|
||||
self::assertCount(43, $this->pages->editorPage('post', -1, 50)['items']);
|
||||
self::assertSame(2, $this->pages->editorPage('page')['total']);
|
||||
self::assertSame(1, $this->pages->editorPage('post', query: 'Eintrag 42')['total']);
|
||||
self::assertSame(0, $this->pages->editorPage('post', query: '%')['total'], 'LIKE wildcards are literal search text.');
|
||||
self::assertSame(0, $this->pages->editorPage('post', query: '_')['total']);
|
||||
$saved = $this->pages->findForEditor($ids[0]);
|
||||
$this->pages->publish($ids[0], $saved['draft_revision_id'], $saved['version']);
|
||||
self::assertSame(1, $this->pages->editorPage('post', status: 'published')['total']);
|
||||
self::assertSame(42, $this->pages->editorPage('post', status: 'draft')['total']);
|
||||
$this->pages->saveDraft($ids[0], DemoContent::pages()['home'], $saved['version'] + 1);
|
||||
self::assertSame(1, $this->pages->editorPage('post', status: 'changed')['total']);
|
||||
$this->db->update('page', ['deleted_at' => gmdate('Y-m-d H:i:s')], ['id' => $ids[42]]);
|
||||
self::assertSame(42, $this->pages->editorPage('post')['total']);
|
||||
self::assertSame(1, $this->pages->editorPage('post', status: 'trash')['total']);
|
||||
self::assertCount(20, $this->pages->choices(type: 'post'));
|
||||
self::assertSame([], $this->pages->choices(id: $ids[42]));
|
||||
self::assertCount(2, $this->pages->choices(parentsOnly: true));
|
||||
self::assertSame('page-2', $this->pages->choices(id: 'page-2', parentsOnly: true)[0]['id']);
|
||||
self::assertSame([], $this->pages->choices(id: $ids[1], parentsOnly: true));
|
||||
}
|
||||
|
||||
public function testDocumentRoundTripAndDraftPrivacy(): void
|
||||
{
|
||||
$document = DemoContent::pages()['home'];
|
||||
@@ -690,11 +727,8 @@ final class CmsTest extends TestCase
|
||||
}
|
||||
}
|
||||
|
||||
public function testASliderAdvancesByItselfOnlyWhenItWasAskedTo(): void
|
||||
public function testSliderControlsLoadOnlyForPagesWithSliders(): void
|
||||
{
|
||||
// Autorotation braucht ein Skript - das einzige auf einer öffentlichen Seite außer den
|
||||
// Tabs. Es kommt nur auf Seiten, die einen solchen Slider tatsächlich enthalten, und die
|
||||
// Richtlinie bleibt sonst bei script-src 'none'.
|
||||
$document = DemoContent::starter('Mit Slider');
|
||||
$document['schemaVersion'] = 17;
|
||||
$document['blocks'][] = ['id' => 'slider', 'type' => 'slider', 'props' => [
|
||||
@@ -703,22 +737,26 @@ final class CmsTest extends TestCase
|
||||
$this->pages->publish('page-1', $this->pages->saveDraft('page-1', $document, 0), 1);
|
||||
|
||||
$ohne = $this->request('/');
|
||||
self::assertStringNotContainsString('slider.js', $ohne->getContent());
|
||||
self::assertStringContainsString("script-src 'none'", (string) $ohne->headers->get('Content-Security-Policy'));
|
||||
self::assertStringContainsString('slider.js?v=', $ohne->getContent());
|
||||
self::assertStringNotContainsString('data-autoplay=', $ohne->getContent());
|
||||
self::assertStringContainsString("script-src 'self'", (string) $ohne->headers->get('Content-Security-Policy'));
|
||||
|
||||
$document['blocks'][array_key_last($document['blocks'])]['props']['autoplay'] = 7;
|
||||
$revision = $this->pages->saveDraft('page-1', $document, $this->pages->findForEditor('page-1')['version']);
|
||||
$this->pages->publish('page-1', $revision, $this->pages->findForEditor('page-1')['version']);
|
||||
|
||||
$mit = $this->request('/');
|
||||
self::assertStringContainsString('<script src="/assets/slider.js?v=', $mit->getContent());
|
||||
self::assertStringContainsString('<script type="module" src="/assets/slider.js?v=', $mit->getContent());
|
||||
self::assertStringContainsString('data-autoplay="7"', $mit->getContent());
|
||||
self::assertStringContainsString("script-src 'self'", (string) $mit->headers->get('Content-Security-Policy'));
|
||||
|
||||
// Das Skript hält sich an die Bewegungsschranke und hört beim ersten Eingriff auf.
|
||||
$skript = (string) file_get_contents(__DIR__.'/../public/assets/slider.js');
|
||||
self::assertStringContainsString("matchMedia('(prefers-reduced-motion: reduce)')", $skript);
|
||||
self::assertStringContainsString("'pointerdown', 'keydown', 'touchstart', 'wheel'", $skript);
|
||||
// Removing the block removes the public dependency and closes the script policy again.
|
||||
array_pop($document['blocks']);
|
||||
$revision = $this->pages->saveDraft('page-1', $document, $this->pages->findForEditor('page-1')['version']);
|
||||
$this->pages->publish('page-1', $revision, $this->pages->findForEditor('page-1')['version']);
|
||||
$plain = $this->request('/');
|
||||
self::assertStringNotContainsString('slider.js', $plain->getContent());
|
||||
self::assertStringContainsString("script-src 'none'", (string) $plain->headers->get('Content-Security-Policy'));
|
||||
}
|
||||
|
||||
public function testTheSamePostsCanBeShownInThreeWays(): void
|
||||
@@ -770,30 +808,19 @@ final class CmsTest extends TestCase
|
||||
(new DocumentValidator())->validate($document);
|
||||
}
|
||||
|
||||
public function testEntranceEffectsRunOnScrollWhereTheBrowserCanAndOnLoadOtherwise(): void
|
||||
public function testEntranceEffectsLoadOnlyWhenUsedAndKeepTheirSelectionInHtml(): void
|
||||
{
|
||||
// Umut: „Effekte gibt es keine“. Es gab sie - aber nur beim Zeigen, nicht beim Laden, und
|
||||
// eine Einblendung beim Laden ist unterhalb der Falz wertlos: sie ist vorbei, bevor
|
||||
// jemand hinsieht. Mit einer Scroll-Zeitachse läuft dieselbe Animation, wenn der
|
||||
// Abschnitt in den Blick kommt - ohne eine Zeile JavaScript.
|
||||
$css = (string) file_get_contents(__DIR__.'/../public/assets/enhancements.css');
|
||||
self::assertStringContainsString('@supports (animation-timeline: view())', $css);
|
||||
self::assertStringContainsString('animation-timeline: view()', $css);
|
||||
|
||||
// Beides steht hinter der Bewegungsschranke, und der Rückfall ist die Fassung von vorher:
|
||||
// ein Browser ohne Scroll-Zeitachse zeigt alles, nur eben beim Laden animiert.
|
||||
$geschuetzt = explode('@media (prefers-reduced-motion: no-preference) {', $css);
|
||||
self::assertStringNotContainsString('animation-timeline', $geschuetzt[0], 'Keine Bewegung außerhalb der Schranke.');
|
||||
self::assertSame(1, substr_count($css, '@supports (animation-timeline: view())'));
|
||||
|
||||
// Der wichtigste Teil, und er ist mit Messen bezahlt: auf dem Scrollweg wird **keine
|
||||
// Deckkraft** animiert. Eine Scroll-Zeitachse ist umkehrbar, also stünde alles unterhalb
|
||||
// der Falz dauerhaft am Anfang der Kurve - bei Deckkraft 0,85 wird aus #3d4d5c ein
|
||||
// #78838d, und damit 3,86:1 statt 7,1:1. axe hat genau das gemeldet.
|
||||
self::assertStringContainsString('@keyframes kant-scroll-rise { from { transform: translateY(20px) } to { transform: none } }', $css);
|
||||
self::assertStringContainsString('@keyframes kant-scroll-fade { from { transform: translateY(10px) } to { transform: none } }', $css);
|
||||
$scrollteil = explode('@supports (animation-timeline: view())', $css)[1];
|
||||
self::assertStringNotContainsString('opacity', explode('.fx-delay-long', $scrollteil)[0], 'Auf dem Scrollweg bleibt der Text voll deckend.');
|
||||
$document = DemoContent::starter('Mit Bewegung');
|
||||
$document['schemaVersion'] = 19;
|
||||
$document['blocks'][0]['layout']['effects']['entrance'] = 'left';
|
||||
$this->pages->publish('page-1', $this->pages->saveDraft('page-1', $document, 0), 1);
|
||||
$response = $this->request('/');
|
||||
self::assertStringContainsString('fx-entrance-left', $response->getContent());
|
||||
self::assertStringContainsString('motion.js?v=', $response->getContent());
|
||||
self::assertStringContainsString("script-src 'self'", (string) $response->headers->get('Content-Security-Policy'));
|
||||
unset($document['blocks'][0]['layout']['effects']['entrance']);
|
||||
$this->pages->publish('page-1', $this->pages->saveDraft('page-1', $document, 2), 3);
|
||||
self::assertStringNotContainsString('motion.js?v=', $this->request('/')->getContent());
|
||||
|
||||
// Und die drei Beitragsvorlagen bringen die Einblendung von sich aus mit.
|
||||
foreach (array_keys(PostLayouts::all()) as $key) {
|
||||
@@ -3307,8 +3334,8 @@ final class CmsTest extends TestCase
|
||||
foreach ($expected as $needle) {
|
||||
self::assertStringContainsString($needle, $html, $type);
|
||||
}
|
||||
// No script is needed for any of them: the public page still forbids scripting.
|
||||
self::assertStringContainsString("script-src 'none'", (string) $this->request('/')->headers->get('Content-Security-Policy'));
|
||||
// Sliders receive controls; the other blocks keep a script-free public page.
|
||||
self::assertStringContainsString('slider' === $type ? "script-src 'self'" : "script-src 'none'", (string) $this->request('/')->headers->get('Content-Security-Policy'));
|
||||
}
|
||||
|
||||
/** @return iterable<string, array{string, array<string, mixed>}> */
|
||||
|
||||
Reference in new issue
Block a user