Files
WP-LIT-MCP/tests/Integration/divi-theme-fonts-smoke.sh
T

144 lines
9.9 KiB
Bash
Executable File
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env bash
# End-to-end checks of Etappe 4d (Divi theme options: read, change, unknown font with suggestions, reset to default,
# snapshot + restore; custom font upload from a public URL) 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-theme-fonts-smoke.sh
# Needs network access from the container (fetches one small woff2 from fonts.gstatic.com). The EXIT trap puts the
# options et_divi and et_uploaded_fonts back byte-exact (serialized, base64 through the environment), removes the
# uploaded font with Divi's own FontsUtility::font_remove (files included) and fails the script when a restore fails.
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)-$$"
FONT_NAME="LIT Smoke $RUN"
FONT_URL="https://fonts.gstatic.com/s/roboto/v30/KFOmCnqEu92Fr1Mu4mxK.woff2"
# $1 = option name. An option that does not exist is captured as the sentinel string, so restore() can delete it again
# (update_option(false) would leave an empty row behind, and Divi's font_add then fails on that '').
capture() { wp eval "echo base64_encode(serialize(get_option('$1', '__lit_absent__')));"; }
ORIGINAL_THEME=$(capture et_divi)
ORIGINAL_FONTS=$(capture et_uploaded_fonts)
[ -n "$ORIGINAL_THEME" ] || { echo "FAIL: could not capture et_divi"; exit 1; }
[ -n "$ORIGINAL_FONTS" ] || { echo "FAIL: could not capture et_uploaded_fonts"; exit 1; }
curl -fsS -m 20 -o /dev/null "$FONT_URL" || { echo "FAIL: $FONT_URL is not reachable from the container"; exit 1; }
restore() { # $1 = option name, $2 = name of the environment variable that holds the capture
local LIT_CAPTURE="$2"; export LIT_CAPTURE
LIT_OPTION="$1" wp eval '$n = getenv("LIT_OPTION"); $v = unserialize(base64_decode(getenv(getenv("LIT_CAPTURE")))); if ($v === "__lit_absent__") { delete_option($n); exit(get_option($n, "__lit_absent__") === $v ? 0 : 1); } update_option($n, $v); exit(serialize(get_option($n)) === serialize($v) ? 0 : 1);' >/dev/null 2>&1 \
|| { echo "FAIL: $1 not restored"; return 1; }
}
cleanup() {
local failed=0
# Remove the font through Divi (deletes the files), then put the option back exactly as it was.
LIT_FONT="$FONT_NAME" wp eval 'ET\Builder\VisualBuilder\Fonts\FontsUtility::font_remove(getenv("LIT_FONT"));' >/dev/null 2>&1 || true
restore et_uploaded_fonts LIT_FONTS || failed=1
restore et_divi LIT_THEME || failed=1
wp eval 'ET_Core_PageResource::remove_static_resources("all", "all", true);' >/dev/null 2>&1 || true
cleanup_app_passwords_for "$ADMIN_LOGIN"
return "$failed"
}
export LIT_THEME="$ORIGINAL_THEME" LIT_FONTS="$ORIGINAL_FONTS"
trap 'rc=$?; cleanup || rc=1; guard_exit "$rc" || exit $?' EXIT
connect "$ADMIN_LOGIN"
# confirm <tool> <input JSON>: dry run must answer confirm_required; then the confirmed call; prints the result.
confirm() {
local dry rev
dry=$(ability "$1" "$2")
[ "$(error_code "$dry")" = "confirm_required" ] || fail "$1 dry run: $dry"
rev=$(echo "$dry" | jq -r '.__error | fromjson | .design_rev')
ability "$1" "$(echo "$2" | jq -c --arg r "$rev" '. + {confirm: true, design_rev: $r}')"
}
theme_option() { # $1 = key -> the stored value ('' when the key is not set)
wp eval "\$o = get_option('et_divi'); echo array_key_exists('$1', \$o) ? var_export(\$o['$1'], true) : '<unset>';"
}
# 1. Read: the tool lists the options with effective values and a design_rev.
GET=$(ability lit/divi-theme-options-get '{}')
echo "$GET" | jq -e '.design_rev | type == "string"' >/dev/null || fail "theme-options-get without design_rev: $GET"
echo "$GET" | jq -e '[.. | objects | select(has("effective"))] | length >= 20' >/dev/null \
|| fail "theme-options-get does not list the 20 options with effective values: $GET"
pass "theme options listed"
# 2. Change two options (snapshot is taken), stale design_rev refused, values stored with Divi's types.
UPDATE=$(jq -cn '{set: {body_font_size: 18, content_width: "1200"}}')
DONE=$(confirm lit/divi-theme-options-update "$UPDATE")
[ "$(echo "$DONE" | jq -r '.ok')" = "true" ] || fail "theme options update: $DONE"
THEME_SNAPSHOT=$(echo "$DONE" | jq -r '.snapshot_id')
[ "$(theme_option body_font_size)" = "18" ] || fail "body_font_size not stored"
[ "$(theme_option content_width)" = "1200" ] || fail "content_width not stored as an integer"
# A different value: an unchanged set is answered as "no change" before design_rev is looked at.
STALE=$(ability lit/divi-theme-options-update "$(echo "$UPDATE" | jq -c '.set.body_font_size = 19 | . + {confirm: true, design_rev: "veraltet"}')")
[ "$(error_code "$STALE")" = "rev_mismatch" ] || fail "stale design_rev: $STALE"
pass "theme options changed ($THEME_SNAPSHOT), stale design_rev refused"
# 3. Validation. Range and unknown keys are already refused by the input schema (before the plugin runs); unknown font
# names and "none" reach the plugin, which collects them with suggestions. Nothing is written in any case.
BAD=$(ability lit/divi-theme-options-update '{"set": {"heading_font": "Robotto", "body_font": "none"}}')
[ "$(error_code "$BAD")" = "validation_failed" ] || fail "invalid input accepted: $BAD"
echo "$BAD" | grep -q 'Roboto' || fail "no font suggestion in: $BAD"
echo "$BAD" | grep -q 'body_font' || fail "none not refused with its path: $BAD"
BAD=$(ability lit/divi-theme-options-update '{"set": {"body_font_size": 99}}')
echo "$BAD" | jq -e 'has("__error")' >/dev/null || fail "out-of-range size accepted: $BAD"
BAD=$(ability lit/divi-theme-options-update '{"set": {"colour": 1}}')
echo "$BAD" | jq -e 'has("__error")' >/dev/null || fail "unknown key accepted: $BAD"
[ "$(theme_option body_font_size)" = "18" ] || fail "invalid call changed body_font_size"
[ "$(theme_option heading_font)" = "<unset>" ] || fail "invalid call changed heading_font"
pass "unknown font with suggestions, none, range and unknown key refused"
# 4. A known Google font is accepted; null resets an option to Divi's default (key removed).
DONE=$(confirm lit/divi-theme-options-update '{"set": {"heading_font": "Roboto", "content_width": null}}')
[ "$(echo "$DONE" | jq -r '.ok')" = "true" ] || fail "font + reset: $DONE"
[ "$(theme_option heading_font)" = "'Roboto'" ] || fail "heading_font not stored"
[ "$(theme_option content_width)" = "<unset>" ] || fail "content_width not removed by null"
pass "Google font stored, null resets to Divi's default"
# 5. Restore the snapshot of step 2: body_font_size and content_width are back as before step 2; heading_font too.
LIST=$(ability lit/divi-design-snapshots '{"area": "theme_options"}')
echo "$LIST" | jq -e --arg s "$THEME_SNAPSHOT" 'any(.snapshots[]; .id == $s)' >/dev/null || fail "snapshot not listed: $LIST"
DONE=$(confirm lit/divi-design-restore "$(jq -cn --arg s "$THEME_SNAPSHOT" '{snapshot_id: $s}')")
[ "$(echo "$DONE" | jq -r '.ok')" = "true" ] || fail "restore: $DONE"
# The snapshot of step 2 was taken before any change of this run: the three keys must equal their original state
# (present with the same typed value, or absent) – compared against the captured et_divi, not against assumptions.
for KEY in body_font_size content_width heading_font; do
LIT_KEY="$KEY" wp eval '$o = unserialize(base64_decode(getenv("LIT_THEME"))); $n = get_option("et_divi"); $k = getenv("LIT_KEY"); exit(array_key_exists($k, $o) === array_key_exists($k, $n) && ($o[$k] ?? null) === ($n[$k] ?? null) ? 0 : 1);' \
|| fail "$KEY not restored to its state before the first change"
done
pass "theme_options snapshot restored (body_font_size, content_width, heading_font as before)"
# 6. Font upload: Probelauf names the file (format, bytes, sha256) and installs nothing.
UPLOAD=$(jq -cn --arg n "$FONT_NAME" --arg u "$FONT_URL" '{name: $n, files: {woff2: $u}, weights: "all"}')
DRY=$(ability lit/divi-fonts-upload "$UPLOAD")
[ "$(error_code "$DRY")" = "confirm_required" ] || fail "font dry run: $DRY"
echo "$DRY" | grep -q 'woff2' || fail "dry run does not name the format: $DRY"
LIT_FONT="$FONT_NAME" wp eval 'exit(isset(get_option("et_uploaded_fonts", [])[getenv("LIT_FONT")]) ? 1 : 0);' \
|| fail "dry run installed the font"
pass "font dry run installs nothing"
# 7. Confirmed upload: the font is in et_uploaded_fonts, the file exists, the name is now reserved.
DONE=$(ability lit/divi-fonts-upload "$(echo "$UPLOAD" | jq -c '. + {confirm: true}')")
[ "$(echo "$DONE" | jq -r '.ok')" = "true" ] || fail "font upload: $DONE"
LIT_FONT="$FONT_NAME" wp eval '$f = get_option("et_uploaded_fonts", [])[getenv("LIT_FONT")] ?? null; exit($f && isset($f["font_url"]["woff2"]) ? 0 : 1);' \
|| fail "font not stored in et_uploaded_fonts"
AGAIN=$(ability lit/divi-fonts-upload "$(echo "$UPLOAD" | jq -c '. + {confirm: true}')")
[ "$(error_code "$AGAIN")" != "" ] || fail "a second upload of the same name was accepted: $AGAIN"
pass "font installed through Divi, same name refused"
# 8. The uploaded font is a valid theme font now; Arial (web-safe) is refused as a new font name.
DONE=$(confirm lit/divi-theme-options-update "$(jq -cn --arg n "$FONT_NAME" '{set: {body_font: $n}}')")
[ "$(echo "$DONE" | jq -r '.ok')" = "true" ] || fail "uploaded font as body_font: $DONE"
# A taken name (web-safe, Google, uploaded) is a conflict, see DiviFontsUploadAbility.
RESERVED=$(ability lit/divi-fonts-upload "$(jq -cn --arg u "$FONT_URL" '{name: "Arial", files: {woff2: $u}, weights: "all"}')")
[ "$(error_code "$RESERVED")" = "conflict" ] || fail "web-safe name accepted: $RESERVED"
pass "uploaded font usable as theme font, web-safe name refused"
# 9. A file whose content does not match its format key is refused (a PNG offered as woff2).
WRONG=$(ability lit/divi-fonts-upload "$(jq -cn --arg n "$FONT_NAME Falsch" '{name: $n, files: {woff2: "https://s.w.org/images/wmark.png"}, weights: "all"}')")
[ "$(error_code "$WRONG")" = "font_invalid" ] || fail "wrong content accepted: $WRONG"
pass "content/format mismatch refused (font_invalid)"
finish