Rebuild DEEG as a fifth installation, add stepped forms and a plugin system, and clear the review backlog
Forms gained steps, conditions, image choices, number fields with units and a summary step; the DEEG price finder uses all of it. Plugins are manifest plus Twig: blocks, settings, containers, routes, presets and theme packs, switched per installation. The review backlog is done: archive pages render through the normal page pipeline, forms and post lists work inside areas and sections, repeated sections get their own ids, breakpoints and colours come from the palette, backup and update work on MySQL and lock each other out. The task worker no longer shortens a process that has no time limit: set_time_limit() sets the limit instead of extending it, which turned an unlimited CLI run into eighty seconds and killed the full test suite far away from the scheduler. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
66ff68abf8
commit
c8a8fa7460
74 files changed
+5352
-243
No files matched your search
+102
-1
@@ -37,7 +37,12 @@ try {
|
||||
activePage = page;
|
||||
const errors = [];
|
||||
page.on('pageerror', error => { errors.push(error.message); console.error('Editor script error:', error.message); });
|
||||
page.on('console', message => { if (message.type() === 'error' && /Content Security Policy/.test(message.text())) errors.push(message.text()); });
|
||||
page.on('console', message => {
|
||||
if (message.type() === 'error' && /Content Security Policy/.test(message.text())) errors.push(message.text());
|
||||
// Jede Fehlermeldung der Konsole ins Protokoll: ein Editor, der bei „Wird geladen“ stehen
|
||||
// bleibt, sagt sonst nichts darüber, warum.
|
||||
if (message.type() === 'error') console.error('Console error:', message.text());
|
||||
});
|
||||
page.on('dialog', dialog => dialog.accept());
|
||||
// Jede angeforderte Skriptdatei mitschreiben. `public/.htaccess` laesst .js ein Jahr lang als
|
||||
// `immutable` zwischenspeichern; eine Datei ohne Fassungskennung waere damit nach einem Update
|
||||
@@ -839,6 +844,102 @@ try {
|
||||
await page.waitForFunction(() => document.querySelector('#property-title')?.value === 'Parallel gespeicherter Titel');
|
||||
checks.push({ name: 'conflict retains local edits, JSON export, explicit server reload', passed: true });
|
||||
|
||||
// --- Plugins: einschalten, Baustein einfuegen, Eigenschaften aus dem Schema, Vorschau ------
|
||||
// Das Referenzplugin "Bildvergleich" liegt im Repository. Aus ist es zuerst; eingeschaltet
|
||||
// erscheint sein Baustein im Werkzeugkasten, das Panel entsteht aus dem Manifest, und die
|
||||
// Vorschau laedt sein Stylesheet - das Skript nicht, wie bei eigenem Code.
|
||||
await page.goto(origin + '/admin/plugins');
|
||||
await axe('plugins');
|
||||
await page.locator('article').filter({ hasText: 'Bildvergleich' }).getByRole('button', { name: 'Einschalten', exact: true }).click();
|
||||
await page.locator('.notice.ok').filter({ hasText: 'eingeschaltet' }).waitFor();
|
||||
await page.screenshot({ path: 'reports/editor/plugins.png', fullPage: true });
|
||||
await page.goto(origin + '/admin/pages/test-home');
|
||||
await tab('Struktur');
|
||||
await page.locator('#structure [data-block-id="headline"]').waitFor();
|
||||
await treeSelect('headline');
|
||||
await addFromLibrary('[data-add="vergleich.bildvergleich"]');
|
||||
await page.locator('#properties-title').filter({ hasText: 'Bildvergleich' }).waitFor();
|
||||
await page.getByLabel('Beschriftung vorher', { exact: true }).fill('Alt');
|
||||
await page.getByLabel('Startposition des Reglers (%)', { exact: true }).fill('30');
|
||||
for (const label of ['Bild vorher', 'Bild nachher']) {
|
||||
await page.locator('.plugin-media-field').filter({ hasText: label }).getByRole('button', { name: 'Bild aus Mediathek wählen', exact: true }).click();
|
||||
await page.locator('#media-dialog [data-media-id]').first().click();
|
||||
await page.locator('#media-insert').click();
|
||||
await page.locator('.plugin-media-field').filter({ hasText: label }).getByRole('button', { name: 'Anderes Bild wählen', exact: true }).waitFor();
|
||||
}
|
||||
await page.locator('#preview-status').filter({ hasText: 'Entwurf ·' }).waitFor();
|
||||
await page.waitForFunction(() => {
|
||||
const doc = document.querySelector('#preview').contentDocument;
|
||||
return Boolean(doc.querySelector('.block-bildvergleich input[type="range"][value="30"]')) && Boolean(doc.querySelector('link[href^="/plugin-assets/vergleich/assets/bildvergleich.css"]')) && !doc.querySelector('script[src^="/plugin-assets/"]');
|
||||
});
|
||||
await page.screenshot({ path: 'reports/editor/plugin-block.png', fullPage: true });
|
||||
await page.locator('#save').click();
|
||||
await page.locator('#save-status').filter({ hasText: 'Entwurf gespeichert' }).waitFor();
|
||||
await page.locator('#publish').click(); await page.locator('#confirm-publish').click();
|
||||
await page.locator('#save-status').filter({ hasText: 'Veröffentlicht' }).waitFor();
|
||||
{
|
||||
const antwort = await page.request.get(origin + '/');
|
||||
const html = await antwort.text();
|
||||
assert.match(html, /class="block-bildvergleich bildvergleich-rund/, 'Plugin-Baustein auf der Website');
|
||||
assert.match(html, /<script src="\/plugin-assets\/vergleich\/assets\/bildvergleich\.js\?v=1\.1\.0" defer><\/script>/);
|
||||
assert.match(antwort.headers()['content-security-policy'] ?? '', /script-src 'self'/);
|
||||
const css = await page.request.get(origin + '/plugin-assets/vergleich/assets/bildvergleich.css');
|
||||
assert.equal(css.status(), 200);
|
||||
}
|
||||
// Einstellungen des Plugins: aus dem Schema erzeugt, gespeichert, auf der Website sichtbar.
|
||||
await page.goto(origin + '/admin/plugins');
|
||||
await page.getByLabel('Standardbeschriftung vorher', { exact: true }).fill('Alt');
|
||||
await page.getByLabel('Griff des Reglers', { exact: true }).selectOption('punkt');
|
||||
await page.getByRole('button', { name: 'Einstellungen speichern', exact: true }).click();
|
||||
await page.locator('.notice.ok').filter({ hasText: 'Einstellungen' }).waitFor();
|
||||
assert.match(await (await page.request.get(origin + '/')).text(), /bildvergleich-griff-punkt/);
|
||||
// Die eigene Adresse des Plugins antwortet als JSON mit den Einstellungen.
|
||||
{
|
||||
const daten = await page.request.get(origin + '/vergleich/daten');
|
||||
assert.equal(daten.status(), 200);
|
||||
assert.equal((await daten.json()).labels.before, 'Alt');
|
||||
}
|
||||
// Die Seitenvorlage des Plugins im Werkzeugkasten, der Container mit seiner Kinderregel.
|
||||
await page.goto(origin + '/admin/pages/test-home');
|
||||
await tab('Struktur');
|
||||
await page.locator('#structure [data-block-id="headline"]').waitFor();
|
||||
await tab('Elemente');
|
||||
await page.locator('#presets [data-preset="plugin:vergleich:abschnitt"]').click();
|
||||
await page.locator('#properties-title').filter({ hasText: 'Sektion' }).waitFor();
|
||||
await page.locator('#preview-status').filter({ hasText: 'Entwurf ·' }).waitFor();
|
||||
await page.waitForFunction(() => document.querySelector('#preview').contentDocument.querySelectorAll('.block-bildvergleich').length >= 2);
|
||||
await treeSelect('headline');
|
||||
await addFromLibrary('[data-add="vergleich.galerie"]');
|
||||
await page.locator('#properties-title').filter({ hasText: 'Vergleichsgalerie' }).waitFor();
|
||||
// Ein Bildvergleich landet in der Galerie; ein Text passt nicht hinein und wird daneben eingefuegt.
|
||||
await addFromLibrary('[data-add="vergleich.bildvergleich"]');
|
||||
await page.locator('#properties-title').filter({ hasText: 'Bildvergleich' }).waitFor();
|
||||
await page.waitForFunction(() => document.querySelector('#preview').contentDocument.querySelector('.block-vergleichsgalerie .block-bildvergleich') !== null);
|
||||
await treeSelect(await page.evaluate(() => document.querySelector('#preview').contentDocument.querySelector('.block-vergleichsgalerie').id));
|
||||
await addFromLibrary('[data-add="text"]');
|
||||
await page.waitForFunction(() => {
|
||||
const doc = document.querySelector('#preview').contentDocument;
|
||||
return doc.querySelector('.block-vergleichsgalerie .block-text') === null && doc.querySelectorAll('.block-text').length > 0;
|
||||
});
|
||||
await page.locator('#undo').click();
|
||||
await page.locator('#undo').click(); await page.locator('#undo').click(); await page.locator('#undo').click();
|
||||
await page.locator('#preview-status').filter({ hasText: 'Entwurf ·' }).waitFor();
|
||||
await page.screenshot({ path: 'reports/editor/plugin-container.png', fullPage: true });
|
||||
|
||||
// Aus: der Baustein bleibt im Dokument, die Website zeigt den Kommentar, der Editor den Platzhalter.
|
||||
await page.goto(origin + '/admin/plugins');
|
||||
await page.locator('article').filter({ hasText: 'Bildvergleich' }).getByRole('button', { name: 'Ausschalten', exact: true }).click();
|
||||
await page.locator('.notice.ok').filter({ hasText: 'ausgeschaltet' }).waitFor();
|
||||
assert.match(await (await page.request.get(origin + '/')).text(), /<!-- Block [^ ]+ \(vergleich\.bildvergleich\) wird von dieser Installation nicht dargestellt\. -->/);
|
||||
await page.goto(origin + '/admin/pages/test-home');
|
||||
await tab('Struktur');
|
||||
await page.locator('#structure [data-block-id="headline"]').waitFor();
|
||||
await page.locator('#structure').getByText('Baustein vergleich.bildvergleich').first().waitFor();
|
||||
await page.goto(origin + '/admin/plugins');
|
||||
await page.locator('article').filter({ hasText: 'Bildvergleich' }).getByRole('button', { name: 'Einschalten', exact: true }).click();
|
||||
await page.locator('.notice.ok').waitFor();
|
||||
checks.push({ name: 'plugin switched on, block inserted from the toolbox, schema panel, assets on the public page, switched off keeps the block', passed: true });
|
||||
|
||||
// Every management screen, not just the page list: they carry tables, disclosures and now the
|
||||
// media grid, and none of that was ever checked automatically before.
|
||||
for (const [name, path] of [
|
||||
|
||||
Reference in new issue
Block a user