test: Ende-zu-Ende-Test der Presets gegen Divi; Guide und Entscheidungsnotiz zu 4a

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
Jan-Niclas Loosen
2026-10-16 17:56:07 +02:00
co-authored by Claude Sonnet 5.5
parent 327f8d2d35
commit 2ed6093f97
3 changed files with 107 additions and 0 deletions
+24
View File
@@ -22,3 +22,27 @@ Regeln:
der Nutzer es ausdrücklich möchte. der Nutzer es ausdrücklich möchte.
3. Gibt es für einen Zweck noch keine Farbe, Variable oder kein Preset, dem Nutzer vorschlagen, es im 3. Gibt es für einen Zweck noch keine Farbe, Variable oder kein Preset, dem Nutzer vorschlagen, es im
Divi-Builder anzulegen, statt feste Werte zu verteilen. Divi-Builder anzulegen, statt feste Werte zu verteilen.
## Presets anlegen, ändern, löschen
Über `mcp-adapter-execute-ability`:
- `lit/divi-preset-get` – ein Preset mit Einstellungen (`attrs` als Pfad → Wert wie bei `lit-divi-node-get`),
verschachtelten Gruppen-Presets und den Seiten, die es verwenden (`usage`).
- `lit/divi-presets-set` – `items` mit `module` (z. B. `"button"`) oder `group` (z. B. `"font-body"`):
- neu: `name` + Einstellungen; ändern: `preset` (Name oder ID), optional `rename`;
- Einstellungen in `attrs` (Vollform, Platzhalter `{{color:…}}`, `{{var:…}}`, `{{font:…}}`) oder aus einem
gestalteten Modul: `from_node` = `{"post_id": 12, "id": "s0.r0.c1.m2"}` (Pfad-ID aus `lit-divi-outline`);
- Presets tragen nur Gestaltung – Texte, Links und Bilder gehören ins Modul (sonst `validation_failed`);
- `group_presets` (nur Modul-Presets), z. B. `{"font": "Button-Schrift"}`; `make_default: true` macht das
Preset zum Standard seines Moduls bzw. seiner Gruppe;
- neue Gruppen-Presets brauchen das Modul, aus dem die Gruppe stammt: `source_module` oder `from_node`.
- `lit/divi-presets-delete` – nur Presets, die nirgends verwendet werden; sonst `in_use` mit den Fundstellen.
- `lit/divi-design-snapshots` / `lit/divi-design-restore` – vor jeder Änderung legt LIT MCP einen Snapshot an
(je Bereich die letzten 20); wiederherstellen mit `snapshot_id`.
Ablauf jeder Änderung: Aufruf ohne `confirm` = Probelauf (`confirm_required` mit `diff`, betroffenen Seiten und
`design_rev`), dem Nutzer die Änderung zeigen, dann derselbe Aufruf mit `confirm: true` und `design_rev`. Hat sich
der Bestand inzwischen geändert (z. B. im Divi-Builder), kommt `rev_mismatch` – neu prüfen.
Rechte: Presets ändern verlangt das Recht, das Design anzupassen, und in Divis Rollen-Editor den Visual Builder.
@@ -30,3 +30,9 @@ Stand 2026-09-28, Brainstorming läuft (Weg: architektonisch → Spec → Plan).
## Danach ## Danach
Ansätze vorschlagen → Entwurf abschnittsweise freigeben → Spec schreiben → Plan (writing-plans) → Umsetzung subagent-driven mit Reviews, Security-Audit am Ende. Ansätze vorschlagen → Entwurf abschnittsweise freigeben → Spec schreiben → Plan (writing-plans) → Umsetzung subagent-driven mit Reviews, Security-Audit am Ende.
## Umsetzung 4a (Plan 2026-10-16-etappe-4a-presets.md)
- Pipeline `DesignChange` (alle Bereiche), Snapshots `SnapshotStore`, Bereich `presets`.
- Tools: lit/divi-preset-get, lit/divi-presets-set, lit/divi-presets-delete, lit/divi-design-snapshots, lit/divi-design-restore (alle indirekt).
- Schreiben über die Schrittfolge von `GlobalPresetController::sync` (Mirror `preset-sync`).
- Offen für 4b: Fundstellen von Standard-Gruppen-Presets (Gruppenliste je Modul), lit/divi-design-detach.
+77
View File
@@ -0,0 +1,77 @@
#!/usr/bin/env bash
# End-to-end checks of Etappe 4a (presets: read, create, change, default, delete, snapshots, restore) through the
# OAuth-protected endpoint against the real Divi. Runs INSIDE the DDEV web container:
# cd /home/janniclas/Projekte/WordPress && timeout 900 ddev exec bash wp-content/plugins/WP-LIT-MCP/tests/Integration/divi-presets-smoke.sh
# Creates presets named "LIT Smoke <run> …" and one draft page; the EXIT trap restores the preset inventory from the
# snapshot taken before the first change and deletes the page (with revisions) by exact ID.
set -euo pipefail
cd /var/www/html
# shellcheck source=lib.sh
source wp-content/plugins/WP-LIT-MCP/tests/Integration/lib.sh
ADMIN_LOGIN=$(wp user list --role=administrator --field=user_login | head -1)
RUN="$(date +%s)-$$"
PREFIX="LIT Smoke $RUN"
ORIGINAL_PRESETS=$(wp eval 'echo wp_json_encode(ET\Builder\Packages\GlobalData\GlobalPreset::get_data());' 2>/dev/null)
PAGE_ID=""
cleanup() {
wp eval '$d = json_decode(getenv("LIT_PRESETS"), true); et_update_option(ET\Builder\Packages\GlobalData\GlobalPreset::option_name(), $d, false, "", "", true);' \
--user="$ADMIN_LOGIN" >/dev/null 2>&1 || echo "WARN: preset inventory not restored"
if [ -n "$PAGE_ID" ]; then wp post delete "$PAGE_ID" --force >/dev/null 2>&1 || true; fi
cleanup_app_passwords_for "$ADMIN_LOGIN"
}
export LIT_PRESETS="$ORIGINAL_PRESETS"
trap 'rc=$?; cleanup; guard_exit "$rc" || exit $?' EXIT
connect "$ADMIN_LOGIN"
# 1. Create a button preset: dry run, then confirm.
NEW=$(jq -cn --arg n "$PREFIX CTA" '{items: [{module: "button", name: $n,
attrs: {"button.decoration.background": {desktop: {color: "#123456"}}}}]}')
DRY=$(ability lit/divi-presets-set "$NEW")
[ "$(error_code "$DRY")" = "confirm_required" ] || fail "create without confirm: $DRY"
REV=$(echo "$DRY" | jq -r '.__error | fromjson | .design_rev')
DONE=$(ability lit/divi-presets-set "$(echo "$NEW" | jq -c --arg r "$REV" '. + {confirm: true, design_rev: $r}')")
[ "$(echo "$DONE" | jq -r '.ok')" = "true" ] || fail "create with confirm: $DONE"
SNAPSHOT=$(echo "$DONE" | jq -r '.snapshot_id')
pass "preset created (snapshot $SNAPSHOT)"
# 2. Read it back through Divi and the tool.
GET=$(ability lit/divi-preset-get "$(jq -cn --arg p "$PREFIX CTA" '{module: "button", preset: $p}')")
ID=$(echo "$GET" | jq -r '.id')
[ "$(echo "$GET" | jq -r '.attrs["button.decoration.background"].desktop.value.color')" = "#123456" ] \
|| fail "preset-get attrs: $GET"
wp eval "\$d = ET\Builder\Packages\GlobalData\GlobalPreset::get_data(); exit(isset(\$d['module']['divi/button']['items']['$ID']['styleAttrs']) ? 0 : 1);" \
|| fail "Divi does not store styleAttrs for $ID"
pass "preset readable, styleAttrs stored"
# 3. A page with the preset renders its class.
PAGE_ID=$(wp post create --post_type=page --post_status=draft --post_title="$PREFIX Seite" \
--post_content="<!-- wp:divi/placeholder --><!-- wp:divi/section --><!-- wp:divi/row --><!-- wp:divi/column {\"module\":{\"decoration\":{\"sizing\":{\"desktop\":{\"value\":{\"flexType\":\"24_24\"}}}}}} --><!-- wp:divi/button {\"modulePreset\":[\"$ID\"],\"button\":{\"innerContent\":{\"desktop\":{\"value\":{\"text\":\"Los\"}}}}} /--><!-- /wp:divi/column --><!-- /wp:divi/row --><!-- /wp:divi/section --><!-- /wp:divi/placeholder -->" \
--porcelain)
HTML=$(wp eval "echo apply_filters('the_content', get_post($PAGE_ID)->post_content);")
echo "$HTML" | grep -q "preset--module--divi-button--$ID" || fail "rendered page lacks the preset class"
pass "page renders preset class"
# 4. Deleting a used preset is refused.
DEL=$(jq -cn --arg p "$ID" '{items: [{module: "button", preset: $p}]}')
[ "$(error_code "$(ability lit/divi-presets-delete "$DEL")")" = "in_use" ] || fail "delete of a used preset"
pass "used preset not deletable"
# 5. Stale design_rev.
STALE=$(ability lit/divi-presets-set "$(echo "$NEW" | jq -c '.items[0].name += " 2" | . + {confirm: true, design_rev: "veraltet"}')")
[ "$(error_code "$STALE")" = "rev_mismatch" ] || fail "stale design_rev: $STALE"
pass "stale design_rev refused"
# 6. Restore the snapshot taken before step 1: the preset is gone again.
DRY=$(ability lit/divi-design-restore "$(jq -cn --arg s "$SNAPSHOT" '{snapshot_id: $s}')")
[ "$(error_code "$DRY")" = "confirm_required" ] || fail "restore dry run: $DRY"
REV=$(echo "$DRY" | jq -r '.__error | fromjson | .design_rev')
RESTORED=$(ability lit/divi-design-restore "$(jq -cn --arg s "$SNAPSHOT" --arg r "$REV" '{snapshot_id: $s, confirm: true, design_rev: $r}')")
[ "$(echo "$RESTORED" | jq -r '.ok')" = "true" ] || fail "restore: $RESTORED"
wp eval "\$d = ET\Builder\Packages\GlobalData\GlobalPreset::get_data(); exit(isset(\$d['module']['divi/button']['items']['$ID']) ? 1 : 0);" \
|| fail "restored inventory still has $ID"
pass "snapshot restored"
finish