Browsertests wiederhergestellt; drei echte Fehler gefunden
Der Playwright-Editortest war durch den Editorumbau kaputt: er steuerte die abgeschafften Auswahllisten. Umgeschrieben auf die freien Bedienelemente - Einheitenfelder, Vererbung über drei Geräte, einzelnes Zurücksetzen, Palettenfarbe, Abstand je Seite, Hover-Regel, freie Rasterspalten und die weiter vorhandenen Bewegungseffekte. Dabei kamen drei Fehler heraus, die ohne Browserlauf unsichtbar sind: 1. Die frei gestalteten Werte wirkten in der Editorvorschau gar nicht. Ein srcdoc-Iframe erbt die Richtlinie des einbettenden Dokuments, und die Verwaltung verbietet Inline-Styles - das erzeugte style-Element wurde stillschweigend verworfen. Ausgeliefert wurde es korrekt, nur die Vorschau log. Jetzt als inertes template plus adoptedStyleSheets. 2. Die Einrückung der Unterseiten in der Seitenliste hat nie funktioniert; sie lief über ein style-Attribut, das dieselbe Richtlinie verwirft. Jetzt über Klassen. 3. Das Theme setzte alle Links auf die Akzentfarbe und überschrieb die kontextabhängigen Farben: 3,08:1 auf dunklem Grund. Die Regel gilt jetzt nur für Links in Textblöcken, die voreingestellte Linkfarbe liegt auf allen hellen Tönen über 4,5:1. Geprüft: 155 PHP-Tests / 579 Assertions unter PHP 8.2.12 und 8.4.22, 15 JavaScript-Modelltests, vollständiger Editortest mit axe (null Verstöße) und Lighthouse 13.4.1 mit je drei Läufen mobil und Desktop, alle fünf Kategorien 100. Offen: npm run test:browser schlägt wegen eines Inhalts fehl - /grundlagen trägt einen im Editor gesetzten Textblock mit 2,75:1. Nicht verändert, weil es eine Nutzerentscheidung ist. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
270cbda9ac
commit
ba56add678
12 files changed
+169
-32
No files matched your search
+72
-11
@@ -87,7 +87,7 @@ try {
|
||||
assert.equal(await page.locator('#structure [data-block-id="headline"]').isVisible(), true);
|
||||
await page.getByRole('button', { name: 'Alle öffnen', exact: true }).click();
|
||||
await page.getByRole('button', { name: 'Design', exact: true }).click();
|
||||
await page.getByLabel('Schriftgröße', { exact: true }).selectOption('hero');
|
||||
|
||||
const frameValue = async (selector, property, expected) => {
|
||||
await page.waitForFunction(({ selector, property, expected }) => {
|
||||
const doc = document.querySelector('#preview').contentDocument;
|
||||
@@ -95,35 +95,81 @@ try {
|
||||
return element && doc.defaultView.getComputedStyle(element)[property] === expected;
|
||||
}, { selector, property, expected });
|
||||
};
|
||||
// Free values: a number plus a unit, not a fixed list. Filling and blurring fires the change.
|
||||
const setLength = async (id, value, unit = 'px') => {
|
||||
await page.locator(`#${id}`).fill(String(value));
|
||||
await page.locator(`#${id}`).blur();
|
||||
if (unit !== 'px') await page.locator(`#${id}`).locator('xpath=following-sibling::select').selectOption(unit);
|
||||
};
|
||||
const openGroup = async name => {
|
||||
const group = page.locator('.design-group').filter({ has: page.locator('summary', { hasText: name }) });
|
||||
if (!(await group.first().evaluate(element => element.open))) await group.first().locator('summary').click();
|
||||
};
|
||||
|
||||
await setLength('style-fontSize', 72);
|
||||
await frameValue('#headline', 'fontSize', '72px');
|
||||
assert.equal(await page.locator('#preview').evaluate(element => element.contentWindow.innerWidth), 1240);
|
||||
|
||||
// Tablet and mobile inherit until they get an own value, and a reset makes them inherit again.
|
||||
await page.getByRole('button', { name: 'Tablet', exact: true }).click();
|
||||
await frameValue('#headline', 'fontSize', '72px');
|
||||
await page.getByLabel('Schriftgröße', { exact: true }).selectOption('display');
|
||||
await setLength('style-fontSize', 52);
|
||||
await frameValue('#headline', 'fontSize', '52px');
|
||||
await page.getByRole('button', { name: 'Mobil', exact: true }).click();
|
||||
await frameValue('#headline', 'fontSize', '52px');
|
||||
await page.getByLabel('Schriftgröße', { exact: true }).selectOption('title');
|
||||
await setLength('style-fontSize', 36);
|
||||
await frameValue('#headline', 'fontSize', '36px');
|
||||
await page.getByRole('button', { name: 'Mobil-Werte zurücksetzen', exact: true }).click();
|
||||
await page.getByRole('button', { name: 'Mobil zurücksetzen', exact: true }).click();
|
||||
await frameValue('#headline', 'fontSize', '52px');
|
||||
await page.getByRole('button', { name: 'Rückgängig', exact: true }).click();
|
||||
await frameValue('#headline', 'fontSize', '36px');
|
||||
|
||||
// A single value can be reset on its own, without touching the rest of the device.
|
||||
await page.getByLabel('Ausrichtung', { exact: true }).selectOption('center');
|
||||
await frameValue('#headline', 'textAlign', 'center');
|
||||
await page.getByRole('button', { name: 'Ausrichtung zurücksetzen', exact: true }).click();
|
||||
await frameValue('#headline', 'textAlign', 'start');
|
||||
await page.getByRole('button', { name: 'Desktop', exact: true }).click();
|
||||
await frameValue('#headline', 'fontSize', '72px');
|
||||
await frameValue('#headline', 'textAlign', 'start');
|
||||
|
||||
// Colours: a palette reference resolves to the theme variable, a hex value is taken as it is.
|
||||
await page.locator('#style-color').locator('xpath=following-sibling::select').selectOption('@link');
|
||||
await frameValue('#headline', 'color', 'rgb(164, 81, 15)');
|
||||
const contrast = page.locator('.contrast-note');
|
||||
await contrast.waitFor();
|
||||
assert.match(await contrast.textContent(), /Kontrast \d/, 'The panel reports a contrast ratio.');
|
||||
|
||||
// Per side spacing on the section, and a hover rule that also covers keyboard focus.
|
||||
await page.locator('#structure [data-block-id="hero"]').click();
|
||||
await page.getByLabel('Innenabstand oben / unten', { exact: true }).selectOption('huge');
|
||||
await page.getByRole('button', { name: 'Design', exact: true }).click();
|
||||
await setLength('style-padding-top', 96);
|
||||
await frameValue('#hero', 'paddingTop', '96px');
|
||||
await page.getByRole('button', { name: 'Mobil', exact: true }).click();
|
||||
await page.getByLabel('Innenabstand oben / unten', { exact: true }).selectOption('medium');
|
||||
await setLength('style-padding-top', 32);
|
||||
await frameValue('#hero', 'paddingTop', '32px');
|
||||
await page.getByRole('button', { name: 'Desktop', exact: true }).click();
|
||||
// The background group is collapsed by default, and the readable field next to the swatch is
|
||||
// what a person types into.
|
||||
await openGroup('Hintergrund');
|
||||
const hoverColor = page.locator('#style-backgroundColor-hover').locator('xpath=following-sibling::input[contains(@class,"color-text")]');
|
||||
await hoverColor.fill('#112233');
|
||||
await hoverColor.blur();
|
||||
// The preview carries its stylesheet as template text, applied by the editor as a constructed
|
||||
// stylesheet; the hover rule has to arrive through that path, not as a style element.
|
||||
await page.waitForFunction(() => {
|
||||
const doc = document.querySelector('#preview').contentDocument;
|
||||
const template = doc?.getElementById('kant-styles')?.content.textContent ?? '';
|
||||
const adopted = [...(doc?.adoptedStyleSheets ?? [])].some(sheet => [...sheet.cssRules].some(rule => rule.selectorText?.includes('#hero:hover')));
|
||||
return template.includes('#hero:hover,#hero:focus-within') && adopted;
|
||||
});
|
||||
checks.push({ name: 'free styles: units, inheritance, single reset, palette colour, per-side spacing, hover rule', passed: true });
|
||||
|
||||
await page.locator('[data-preset="columns"]').click();
|
||||
const presetSection = await page.locator('#structure .tree-item[aria-pressed="true"]').getAttribute('data-block-id');
|
||||
const presetRow = `#${presetSection} .block-row`;
|
||||
await frame.locator(presetRow).waitFor();
|
||||
// The preset carries its own responsive tokens: one column on mobile, two from tablet up.
|
||||
await page.getByRole('button', { name: 'Mobil', exact: true }).click();
|
||||
const columnCount = async expected => {
|
||||
await page.waitForFunction(({ selector, expected }) => {
|
||||
const doc = document.querySelector('#preview').contentDocument;
|
||||
@@ -177,15 +223,30 @@ try {
|
||||
await page.getByRole('button', { name: 'Rückgängig', exact: true }).click();
|
||||
await page.locator(`#structure [data-block-id="${galleryId}"]`).click();
|
||||
await page.getByRole('button', { name: 'Design', exact: true }).click();
|
||||
await page.getByLabel('Spaltenaufteilung', { exact: true }).selectOption('two');
|
||||
await openGroup('Layout');
|
||||
await page.locator('#style-gridColumns').fill('2');
|
||||
await page.locator('#style-gridColumns').blur();
|
||||
const columnsOfGallery = async expected => {
|
||||
await page.waitForFunction(({ id, expected }) => {
|
||||
const doc = document.querySelector('#preview').contentDocument;
|
||||
const element = doc?.querySelector(`#${id}`);
|
||||
return element && doc.defaultView.getComputedStyle(element).gridTemplateColumns.split(' ').length === expected;
|
||||
}, { id: galleryId, expected });
|
||||
};
|
||||
await columnsOfGallery(2);
|
||||
// Motion presets are keyframes, which the free system cannot express, so they stay available.
|
||||
await openGroup('Bewegung und Effekte');
|
||||
await page.getByLabel('Schatten', { exact: true }).selectOption('soft');
|
||||
await page.getByLabel('Hover-Effekt', { exact: true }).selectOption('lift');
|
||||
await page.getByLabel('Einblendung beim Laden', { exact: true }).selectOption('rise');
|
||||
await frame.locator(`#${galleryId}.fx-entrance-rise`).waitFor();
|
||||
await page.getByRole('button', { name: 'Mobil', exact: true }).click();
|
||||
await page.getByLabel('Spaltenaufteilung', { exact: true }).selectOption('one');
|
||||
await frame.locator(`#${galleryId}.d-mobile-columns-one`).waitFor();
|
||||
await openGroup('Layout');
|
||||
await page.locator('#style-gridColumns').fill('1');
|
||||
await page.locator('#style-gridColumns').blur();
|
||||
await columnsOfGallery(1);
|
||||
await page.getByRole('button', { name: 'Desktop', exact: true }).click();
|
||||
await columnsOfGallery(2);
|
||||
await page.emulateMedia({ reducedMotion: 'reduce' });
|
||||
await frameValue(`#${galleryId}`, 'animationName', 'none');
|
||||
await frameValue(`#${galleryId}`, 'transform', 'none');
|
||||
@@ -258,7 +319,7 @@ try {
|
||||
assert.equal(await publicPage.evaluate(() => document.documentElement.scrollWidth <= innerWidth), true);
|
||||
await publicPage.screenshot({ path: 'reports/editor/designed-public-mobile.png', fullPage: true });
|
||||
const contentAudit = await new AxeBuilder({ page: publicPage }).withTags(['wcag2a', 'wcag2aa', 'wcag21aa', 'best-practice']).analyze();
|
||||
assert.deepEqual(contentAudit.violations.map(v => v.id), []);
|
||||
assert.deepEqual(contentAudit.violations.map(v => v.id), [], JSON.stringify(contentAudit.violations.map(v => ({ id: v.id, nodes: v.nodes.map(n => ({ target: n.target, summary: n.failureSummary?.split('\n')[1] })) })), null, 1));
|
||||
checks.push({ name: 'edited public renderer accessibility', violations: 0 });
|
||||
await publicPage.close();
|
||||
checks.push({ name: 'add, duplicate, move, delete, undo, exact reload, separate publication, dialog focus', passed: true });
|
||||
|
||||
Reference in new issue
Block a user