From 2ed6093f97b12fdcfc6523f125c56800c661bb18 Mon Sep 17 00:00:00 2001 From: Jan-Niclas Loosen Date: Fri, 16 Oct 2026 17:56:07 +0200 Subject: [PATCH] test: Ende-zu-Ende-Test der Presets gegen Divi; Guide und Entscheidungsnotiz zu 4a Co-Authored-By: Claude Sonnet 5.5 --- docs/guides/divi-design-system.md | 24 ++++++ ...6-09-28-etappe-4-entscheidungen-entwurf.md | 6 ++ tests/Integration/divi-presets-smoke.sh | 77 +++++++++++++++++++ 3 files changed, 107 insertions(+) create mode 100755 tests/Integration/divi-presets-smoke.sh diff --git a/docs/guides/divi-design-system.md b/docs/guides/divi-design-system.md index 6bcf720..91a5652 100644 --- a/docs/guides/divi-design-system.md +++ b/docs/guides/divi-design-system.md @@ -22,3 +22,27 @@ Regeln: 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 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. diff --git a/docs/superpowers/notes/2026-09-28-etappe-4-entscheidungen-entwurf.md b/docs/superpowers/notes/2026-09-28-etappe-4-entscheidungen-entwurf.md index 91703b5..8578c87 100644 --- a/docs/superpowers/notes/2026-09-28-etappe-4-entscheidungen-entwurf.md +++ b/docs/superpowers/notes/2026-09-28-etappe-4-entscheidungen-entwurf.md @@ -30,3 +30,9 @@ Stand 2026-09-28, Brainstorming läuft (Weg: architektonisch → Spec → Plan). ## Danach 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. diff --git a/tests/Integration/divi-presets-smoke.sh b/tests/Integration/divi-presets-smoke.sh new file mode 100755 index 0000000..c2f338f --- /dev/null +++ b/tests/Integration/divi-presets-smoke.sh @@ -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 …" 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="" \ + --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